Android——模块化加载
Posted 化作孤岛的瓜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——模块化加载相关的知识,希望对你有一定的参考价值。
其实google发布app bundle已经是18年的事情了,只是一直在业务上接触不到。最近刚好打算用到爱奇艺的Qigsaw框架来做国内的模块化加载,所以打算一起学习一下。
说到模块化加载。其实本质就是为了缩小apk大小,增加下载量,增加应用功能的自由装载机制。
本文会先从官网的android app bundle文档与实例开始分析,先快速入手,写一个最简单的模块化调用demo。
1.分析官网的demo:
演示了如何使用 PlayCore API 请求和下载功能模块:
https://github.com/android/app-bundle-samples/tree/main/DynamicFeatures
演示了从安装的功能模块安全访问代码的三种不同方法:
https://github.com/googlesamples/android-dynamic-code-loading
2.首先新建一个工程,名字叫PlayFeatureDemo
导入依赖:api "com.google.android.play:core:1.8.3"
3.file->new module->Dynamic Feature ->输入module名字,game->next->finish
创建一个GameSampleActivity
4.创建SplitInstallManager实例,配置监听器,发起跳转。
代码:
package com.ng.playfeaturedemo;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.google.android.play.core.splitinstall.SplitInstallManager;
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
import com.google.android.play.core.splitinstall.SplitInstallRequest;
import com.google.android.play.core.splitinstall.SplitInstallSessionState;
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener;
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
import com.google.android.play.core.tasks.OnFailureListener;
import com.google.android.play.core.tasks.OnSuccessListener;
public class MainActivity extends BaseSplitActivity implements SplitInstallStateUpdatedListener {
private TextView mProgressBar;
private SplitInstallManager manager;
private String PACKAGE_NAME = "com.ng.game";
private String KOTLIN_SAMPLE_CLASSNAME = "com.ng.game.GameSampleActivity";
private int CONFIRMATION_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = findViewById(R.id.progressBar);
manager = SplitInstallManagerFactory.create(this);
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadAndLaunchModule(getString(R.string.module_feature_game));
}
});
}
private void loadAndLaunchModule(String string) {
if (manager.getInstalledModules().contains(string)) {
Toast.makeText(this, "已安装", Toast.LENGTH_SHORT).show();
launchActivity(KOTLIN_SAMPLE_CLASSNAME);
return;
}
SplitInstallRequest request = SplitInstallRequest.newBuilder().addModule(string).build();
manager.startInstall(request).addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
toastAndLog("成功: " + result);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
toastAndLog("失败: " + e.getMessage());
}
});
}
private void launchActivity(String className) {
Intent intent = new Intent().setClassName(BuildConfig.APPLICATION_ID, className);
startActivity(intent);
}
@Override
protected void onResume() {
manager.registerListener(this);
super.onResume();
}
@Override
protected void onPause() {
manager.unregisterListener(this);
super.onPause();
}
@Override
public void onStateUpdate(SplitInstallSessionState state) {
//是否多模块安装
boolean multiInstall = state.moduleNames().size() > 1;
switch (state.status()) {
case SplitInstallSessionStatus.DOWNLOADING:
displayLoadingState(state, "下载中 ");
case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION:
try {
manager.startConfirmationDialogForResult(state, this, CONFIRMATION_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
break;
case SplitInstallSessionStatus.INSTALLED:
launchActivity(KOTLIN_SAMPLE_CLASSNAME);
break;
case SplitInstallSessionStatus.INSTALLING:
displayLoadingState(state, "安装进度");
break;
case SplitInstallSessionStatus.FAILED:
toastAndLog(getString(R.string.error_for_module, state.errorCode(),
state.moduleNames()));
break;
}
}
private void displayLoadingState(SplitInstallSessionState state, String message) {
int max = (int) state.totalBytesToDownload();
int progress = (int) state.bytesDownloaded();
mProgressBar.setText(message + " " + progress + " / " + max);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == CONFIRMATION_REQUEST_CODE) {
// Handle the user's decision. For example, if the user selects "Cancel",
// you may want to disable certain functionality that depends on the module.
if (resultCode == Activity.RESULT_CANCELED) {
toastAndLog("用户拒绝");
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
void toastAndLog(String text) {
Toast.makeText(this, text, Toast.LENGTH_LONG).show();
Log.d("MainActivity", text);
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
}
本地测试:
api文档:https://developer.android.com/guide/app-bundle/test/testing-fakesplitinstallmanager
生成aab之后:
拆包:
https://blog.csdn.net/qq_36168049/article/details/101002012
拆包命令:
bundletool build-apks --local-testing --bundle=/Users/xiaoguagua/AndroidProjects/OpenSourceProjects/PlayFeatureDemo/app/release/app-release.aab --output=/Users/xiaoguagua/AndroidProjects/OpenSourceProjects/PlayFeatureDemo/app/release/my_app.apks
得到了一个my_app.apks,可以解压
安装到手机:
bundletool install-apks --apks=/Users/xiaoguagua/AndroidProjects/OpenSourceProjects/PlayFeatureDemo/app/release/my_app.apks
运行,点击按钮:
可以看到开始下载组件
然后自动跳转到写的组件界面
就大功告成了。接下来会开始分析 实现原理以及Qigsaw的实现步骤。
以上是关于Android——模块化加载的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段