本文对爱奇艺框架Qigsaw插件部分进行了源码分析。
《咏柳》
碧玉妆成一树高,万条垂下绿丝绦。
不知细叶谁裁出?二月春风似剪刀。
-唐代,贺知章
简介
Qigsaw是一套基于Android App Bundles实现的Android动态组件化方案,它无需应用重新安装即可动态分发插件。
备注:本文基于master分支d9066cc 2020.02.15分析
插件
Qigsaw提供了Gradle插件可以应用在application和dynamic-feature上。如果你想在编译期间将split apk上传到cdn服务器,可以参考SampleSplitApkUploader.groovy自己实现上传逻辑。接下来我们根据Qigsaw提供的demo分析一下Gradle插件的实现,demo中提供了3个feature module,如下图所示:
application插件分析
插件提供了几个任务分别是generateQigsawConfigTask、qigsawInstallTask、qigsawAssembleTask以及qigsawUploadSplitApkTask。
qigsawInstallTask依赖于qigsawAssembleTask的执行,当qigsawAssembleTask执行完成后使用adb完成安装操作。qigsawAssembleTask任务依赖关系如下图,copySplitManifestTask会将dynamic feature的清单文件复制到指定文件夹,copySplitApkTask会将打包成功后的apk复制到指定的文件夹。qigsawAssembleTask任务会收集split的信息存储为json文件,最后会将json和apk文件复制到mergeAssetsTask任务的输出文件夹,apk文件后缀会被修改为zip。
qigsawAssembleTask任务执行完成后会调用app的assembleTask完成打包,可以看到apk的assets目录中存放了各feature的apk(改为了zip后缀)。
json文件中存放了split的各种信息:
dynamic-feature插件分析
QigsawDynamicFeaturePlugin插件中注册了两个Transform。
QigsawDynamicFeaturePlugin.groovy
resourcesLoaderTransform = new SplitResourcesLoaderTransform(project)
SplitLibraryLoaderTransform libraryLoaderTransform = new SplitLibraryLoaderTransform(project)
android.registerTransform(resourcesLoaderTransform)
android.registerTransform(libraryLoaderTransform)
一个是SplitResourcesLoaderTransform,使用asm对activity、service、receiver进行了处理。主要实现逻辑在SplitResourcesLoaderInjector类中:
SplitResourcesLoaderInjector.groovy
byte[] injectClass(Path path, String className) {
byte[] ret = null
if (isActivity(className)) {
println("Inject activity " + className)
ret = new SplitActivityWeaver().weave(path.newInputStream())
} else if (isService(className)) {
println("Inject service " + className)
ret = serviceWeaver.weave(path.newInputStream())
} else if (isReceiver(className)) {
println("Inject receiver " + className)
ret = receiverWeaver.weave(path.newInputStream())
}
return ret
}
针对activity组件重写了getResources方法插入了以下代码:
public Resources getResources() {
SplitInstallHelper.loadResources(this, super.getResources());
return super.getResources();
}
针对service组件在onCreate方法中调用了 SplitInstallHelper.loadResources方法,针对receiver组件在onReceive方法中调用了SplitInstallHelper.loadResources方法。
另一个是SplitLibraryLoaderTransform,使用asm新增了一个名为工程名+SplitLibraryLoader的类:
SplitLibraryLoaderTransform.groovy
class SplitLibraryLoaderTransform extends SimpleClassCreatorTransform {
...
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
super.transform(transformInvocation)
transformInvocation.getOutputProvider().deleteAll()
def dest = prepareToCreateClass(transformInvocation)
createSimpleClass(dest, "com.iqiyi.android.qigsaw.core.splitlib." + project.name + "SplitLibraryLoader",
"java.lang.Object", new SimpleClassCreatorTransform.OnVisitListener() {
@Override
void onVisit(ClassWriter cw) {
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "loadSplitLibrary", "(Ljava/lang/String;)V", null, null)
mw.visitVarInsn(Opcodes.ALOAD, 1)
mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "loadLibrary", "(Ljava/lang/String;)V", false)
mw.visitInsn(Opcodes.RETURN)
mw.visitMaxs(2, 1)
mw.visitEnd()
System.out.println()
}
})
}
}
生成的类如下:
public class javaSplitLibraryLoader {
public javaSplitLibraryLoader() {
}
public void loadSplitLibrary(String var1) {
System.loadLibrary(var1);
}
}
总结
Qigsaw提供了两种插件,一种应用在application工程上,主要作用是新增了几个打包相关的Task,执行打包操作前会先对feature库进行打包,然后会收集split的信息存储为json文件,将json文件和apk文件复制到mergeAssetsTask任务的输出文件夹,apk文件后缀会被修改为zip,最后执行application的打包Task,这样最终会将feature apk和相关json信息内置到assets目录下;另一种是应用在dynamic-feature工程上,主要作用是使用asm对feature库的activity、service、receiver组件插入了获取资源的相关代码SplitInstallHelper.loadResources
,另外新增了一个名为工程名+SplitLibraryLoader的类。
参考
- https://github.com/iqiyi/Qigsaw