最新依赖-jitpack
Google的AAB出来好久了,但在国内一直没法用。知道Qigsaw后就开始学习,弄了好久都没学会,太烦了中途都放弃了。后来有时间了,又来学习这个,因为真实用啊!
直到学会了,还有问题
- 编译后无法Clean
- 不支持最新的 'com.android.tools.build:gradle:4.1.3'
- Demo代码太繁琐,不容易懂
- 使用起来也比较麻烦
学会后,我觉得不应该这样麻烦,是可以非常简单的!
可以把Qigsaw拆分为运行框架和打包工具,因此有了QigsawBundle。
DynamicProvider
Provider在应用启动时就会被调用,Dynamic中的Provider第一次启动是找不到的,应用会直接报错,无法启动。Qigsaw为每个DynamicProvider生成了一个装饰类,找不到原DynamicProvider时,就调用装饰类。
QigsawBundle不生成任何装饰类,而是通过工具DynamicProviderSwitch自动把DynamicProvider设置为关闭状态(android:enabled="false")。启动应用时,再读取manifest中哪些DynamicProvider类存在,如果存在则启动Provider,不存在则忽略。Split安装后重试未启动成功的DynamicProvider。
DynamicProviderSwitch:编译时自动关闭Split中的Provider,应用启动时,启动存在的DynamicProvider。
DynamicProviderSwitch支持Google的AAB,可以直接用在海外版的App中
Activity/Service/BroadcastReceiver
Qigsaw打包时,织入了一些代码。经测试证明,不需要在编译期间织入代码的,
Activity/Service/BroadcastReceiver都可以通过运行时代码解决。
可以在ActivityLifecycleCallbacks.onActivityPreCreated中loadResources,
另外两个使用的application.resources ,所以不需要处理。
以下代码都测试通过了。
override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
SplitInstallHelper.loadResources(activity, activity.resources)
}
Service.resources ==applicationContext.resources//true
BroadcastReceiver.context.resources==context.applicationContext.resources//true
onActivityPreCreated 只有Android10+才支持,之前的版本没有用。所以需要用织入代码的方式来解决。
QigsawBundle在onActivityPreCreated中注入resources,因此省去了编译期织入代码。
private Qigsaw(Context context, Downloader downloader, @NonNull SplitConfiguration splitConfiguration) {
this.context = context;
this.downloader = downloader;
this.splitConfiguration = splitConfiguration;
this.currentProcessName = ProcessUtil.getProcessName(context);
this.isMainProcess = context.getPackageName().equals(currentProcessName);
InjectActivityResource.inject((Application) context);//QigsawBundle注入resources
}
CompatBundle
为了让独立打包的Split能运行起来,需要Qigsaw框架做一些兼容处理。如果不实现以下接口,则完全按照标准的Qigsaw方式运行。具体使用参考Demo。
public interface ICompatBundle {
/**
* for 'parseSplitContentsForDefaultVersion'
*/
@Nullable
String readDefaultSplitVersionContent(@NonNull Context context, @NonNull String fileName);
@NonNull
String getMD5(@NonNull File file);
@NonNull
String getMD5(@NonNull InputStream inputStream);
/**
* 在onActivityPreCreated中注入resources
*/
boolean injectActivityResource();
/**
* 因为没有生成任何装饰类,所以ComponentInfoManager是没有数据的,所以需要禁用了
*/
boolean disableComponentInfoManager();
/**
* 没有自动生成的 qigsawConfig,需要指定一个自己创建的
*/
Class<?> qigsawConfigClass();
}
ApkMd5
QigsawBundle中所有Apk计算MD5都使用的ApkMd5,源码也在项目中。
ApkMd5:"AndroidManifest.xml"去掉版本号,去掉"META-INF/BNDLTOOL.RSA"、 "META-INF/BNDLTOOL.SF"、"META-INF/MANIFEST.MF",所有文件排序,再计算APK的MD5。
QigsawBundle可以做到:只修改Base的版本号(versionName&versionCode),然后打包。所有Split的ApkMd5值都是固定的,仅Base包变了。proguard存在一个BUG,需要特别方法才能做到这样。具体请参考 稳定混淆App
DEMO
有时间了会写个QigsawBundle使用介绍,先写了个原理。
DEMO
最后
如果本文帮助到了你,也帮我点个赞吧!
如果你愿意,还可以赞赏一杯咖啡或一瓶水,非常感觉你的慷慨!