android——qigsaw源码分析编译过程(代码片段)

化作孤岛的瓜 化作孤岛的瓜     2022-11-30     739

关键词:

前言

本文主要是对Qigsaw的源码分析。Qigsaw是一种动态组件化技术,可以有效减少包size,进行热更新等调优功能,在实际应用开发中能带来不错的收益与启发。

本文基于2021/7/14版本分析(https://github.com/iqiyi/Qigsaw)

Qigsaw分为 编译过程 → 初始化过程 → 加载过程 → 安装过程 进行源码分析。

如何调试gradle:

Android如何创建Gradle插件开发工程及调试_xialonghua的专栏-CSDN博客_gradle插件开发

目录

包目录结构说明:

1 QigsawAppBasePlugin

1.1 Transform部分:

1.2 Task部分

2 QigsawDynamicFeaturePlugin

2.1 SplitLibraryLoaderTransform

总结


包目录结构说明:

app:测试工程

assets:测试用资源模块

java:测试用java模块

native:测试用native模块

downloader:下载模块

buildSrc:编译期module

playcorelibrary:google app bundle 套件

下面的都是加载和安装过程组件

splitcommon

splitcore

splitdownloader

splitextension

splitinstaller

splitloader

splitreporter

splitrequester

编译过程的主要逻辑在buildSrc的模块中。

META-INF.gradle-plugins下一共注册了两个plugin,其中

QigsawAppBasePlugin:主要给app工程使用(apply plugin: 'com.iqiyi.qigsaw.application')

QigsawDynamicFeaturePlugin:主要给split工程使用(apply plugin: 'com.iqiyi.qigsaw.dynamicfeature')

1 QigsawAppBasePlugin

首先设置qigsawSplit配置参数,包括版本号,mappging文件,是否将插件上传cdn等。

1.1 Transform部分:

注册了SplitComponentTransform和SplitResourcesLoaderTransform两个Transform。

(1) 注册 [SplitComponentTransform]

主要负责记录所有动态库的四大组件。

  • 定义了一个文件:splitManifestParentDir,路径为:Qigsaw/app/build/intermediates/qigsaw/split-outputs/manifests/debug,里面存着动态库的manifest,然后遍历定义的三个动态库:dynamicFeatures = [':java', ':assets', ':native']从splitManifestParentDir中遍历各个动态库的manifest,把application和四大组件信息都存在内存
  • 然后在路径intermediates/transforms/processSplitComponent/debug/50/com/iqiyi/android/qigsaw/core/extension下通过ASM创建一个ComponentInfo类,并把除ContentProvider外的其他信息构建为常量属性:
public class ComponentInfo 
    public static final String native_ACTIVITIES = "com.iqiyi.qigsaw.sample.ccode.NativeSampleActivity";
    public static final String java_ACTIVITIES = "com.iqiyi.qigsaw.sample.java.JavaSampleActivity";
    public static final String java_APPLICATION = "com.iqiyi.qigsaw.sample.java.JavaSampleApplication";

    public ComponentInfo() 
    

为ContentProvider单独构建代理类:

Qigsaw/app/build/intermediates/transforms/processSplitComponent/debug/50/com/iqiyi/qigsaw/sample/java

public class JavaContentProvider_Decorated_java extends SplitContentProvider 
    public JavaContentProvider_Decorated_java() 
    

(2) 注册 [SplitResourcesLoaderTransform]

主要负责在各种组件注入loadResources方法

使用到了WaitableExecutor 提供并发编译能力,提高编译速度

  • 如果是基线包,则获取build.gradle中输入的baseContainerActivities参数(需要加载插件包resource资源的Activity数组),插件包则遍历mergedManifest,获取四大组件Set。
  • 将获取的参数通过SplitResourcesLoaderInjector进行处理,SplitResourcesLoaderInjector中包含:

1.SplitActivityWeaver

在Activity内所有getResouces方法中注入SplitInstallHelper.loadResources方法。

2.SplitServiceWeaver

在Services内的onCreate方法中注入SplitInstallHelper.loadResources方法,没有onCreate就注入一个。

3.SplitReceiverWeaver

在Receiver的onReceiver方法中注入SplitInstallHelper.loadResources方法。

(3) SplitInstallHelper类

主要是收集Split中四大组件和application中的Resources,并实现全局Resouces替换。目的是为了防止加载的动态split,出现资源找不到的问题。

通过SplitCompatResourcesLoader(实现接口SplitResourcesLoader)来实现功能。

SplitResourcesLoader:

public interface SplitResourcesLoader 
    void loadResources(@NonNull Context context, @NonNull Resources resources) throws Throwable;

    void loadResources(@NonNull Context context, @NonNull Resources preResources, @NonNull String splitApkPath) throws Throwable;
  1. 首先resources.getAssets()得到AssetManager,然后反射调用getApkAssets获取ApkAsset,再反射调用其getAssetPath获取以及加载的资源路径:assetPaths。
  2. 此时会去比较已经加载split的资源路径splitResPath,不包含则执行下一步 installSplitResDirs(final Context context, final Resources resources, final List<String> splitResPaths)
  1. api21以上,反射AssetManager的addAssetPath去添加路径。
  2. 21以下,处理比较复杂,需要首先获取所有已加载资源的资源路径resDirs,然后把split(插件)的所有资源路径splitResPaths插在头部,然后以此构建一个新的AssetManager,再用其构建一个新的Resouces,然后就是实现全局的Resouces刷新。
  1. 遍历当前Context和ActivityThread中的所有mActivities,设置新的Resouces。
  2. 获取ActivityThread和ResourcesManager中所有的Resouces,然后遍历替换之前老的Resouces。
  1. 获取ActivityThread中的LoadedApk(在mPackages和mResourcePackages中),替换其中老的mResources对象。

实现全局Resouces替换,在21以上只需要通过AssetManager去添加。在低版本需要把全局Activity,Resources,以及ActivityThread中的LoadedApk所有的Resources都替换成新的。

Transform部分总结:

主要是通过ASM记录了Application和四大组件信息(包括Manifest)到临时文件,然后在所有用到Resources的地方做了hook,防止出现运行时资源找不到的问题。

1.2 Task部分

在Transform部分工作完成之后,QigsawAppBasePlugin的Transform配置工作已经完成,在project.afterEvaluate中开始执行各种task逻辑。

首先初始化了许多task和file的临时变量,然后按以下执行顺序(执行qigsawAssembleDebug的顺序)开始执行Task:

(1) 单个split的 [copySplitManifest Task]

获取intermediates/merged_manifests/release/AndroidManifest.xml 并转移到

intermediates/qigsaw/split-outputs/manifests/debug 中 (存疑,怎么做的拆分?)

(2) 单个split的 [processSplitApk Task]

在assembleTask执行后执行,将split生成的apk:

Qigsaw/features/assets/build/outputs/apk/debug/assets-debug.apk

解压至临时文件区:

/Qigsaw/app/build/intermediates/qigsaw/split-outputs/unzip/debug/assets

然后再签名,打包,生成单个的apk包:

Qigsaw/app/build/intermediates/qigsaw/split-outputs/apks/debug/assets-master.apk

和一个json文件记录信息:

Qigsaw/app/build/intermediates/qigsaw/split-outputs/split-info/debug/assets.json


	"splitName": "assets",
	"builtIn": true,
	"onDemand": true,
	"version": "1.0@1",
	"minSdkVersion": 14,
	"dexNumber": 2,
	"apkData": [
		"abi": "master",
		"url": "assets://qigsaw/assets-master.zip",
		"md5": "bb164a5ba248cbbc05dc9f74a1d1ba41",
		"size": 12804
	]

(3) ExtractTargetFilesFromOldApk [extractTargetFilesFromOldApk(Debug/Release) Task]

如果在参数中设置有oldApk,则把它复制到路径intermediates/qigsaw/old-apk/target-files/Debug下。

(4) GenerateQigsawConfig [generate(Debug/Release)QigsawConfig Task]

构建QigsawConfig,比较简单不赘述。

(5) CreateSplitDetailsFileTask [qigsawAssemble(Debug/Release) Task]

重点来了,这个是文档中提到的(如何运行)构建base APK和split APK的打包Task,qigsawAssembleDebug(发布用Release)

  • 保存abi信息json到assets目录
  • 获取所有split的信息SplitDetails,存储至Qigsaw/app/build/intermediates/qigsaw/split-details/debug/qigsaw_1.0.0_1.0.0.json内

以及更新记录_update_record_.json。

  • 然后开始遍历所有的split,判断新的split名字和老的(上次编译的)是否一样,如果有变化则会标记更新了状态splitDetails.updateRecord.updateMode = SplitDetails.UpdateRecord.VERSION_CHANGED
  • 如果是没有更新(首次安装),则把当前的split的apk从

intermediates/qigsaw/split-outputs/apks/debug/java-master.apk

拷贝至打包的路径下,格式改为zip:

intermediates/merged_assets/debug/out/qigsaw/java-master.zip

Task部分总结:

主要是将split构建的apk,转移到本地的安装目录下,以备后面的安装过程使用。

2 QigsawDynamicFeaturePlugin

注册了SplitResourcesLoaderTransform (QigsawAppBasePlugin中有,刚分析过,不再赘述)与SplitLibraryLoaderTransform

2.1 SplitLibraryLoaderTransform

通过asm创建类名为"com.iqiyi.android.qigsaw.core.splitlib." + project.name + "SplitLibraryLoader"的类。

总结

编译过程主要是做了几件事情:

1.记录application和四大组建信息,resource插桩(热更新常见方式)

2.拷贝split的apk文件到指定目录,为后面的加载做准备

下一篇将会继续分析初始化过程

链接:

Android——Qigsaw 源码分析(一) 编译过程_化身孤岛的瓜-CSDN博客

Android——Qigsaw 源码分析(二) 初始化过程_化身孤岛的瓜-CSDN博客

Android——Qigsaw 源码分析(三) 加载过程_化身孤岛的瓜-CSDN博客

Android——Qigsaw 源码分析(四) 安装过程_化身孤岛的瓜-CSDN博客

qigsaw源码分析编译过程(代码片段)

...过程→加载过程三个阶段进行源码分析。如何调试gradle:Android如何创建Gradle插件开发工程及调试_xialonghua的专栏-CSDN博客_gradle插件开发目录包目录结构说明:1QigsawAppBasePlugin1.1Transform部分:1.2Task部分2QigsawDynamicFeaturePlugin2.1SplitLibraryLoa... 查看详情

android——qigsaw源码分析初始化过程(代码片段)

上一章节描述了Qigsaw编译期做的事情,后面的工作就是运行期进行的初始化,加载,和安装插件的过程。本文主要分析初始化过程。通过官方文档:https://github.com/iqiyi/Qigsaw/blob/master/README.zh-CN.md中可以了解到,插件... 查看详情

android——qigsaw源码分析安装过程(代码片段)

前言在编译,初始化,加载的准备工作都结束以后,就到了插件的安装步骤。本文按调用顺序来分析具体的安装步骤。在官方demo下,可以看到QigsawInstaller中实际执行安装的步骤:SplitInstallManager.startInstall↓SplitInstall... 查看详情

android——qigsaw源码分析初始化过程(代码片段)

上一章节描述了Qigsaw编译期做的事情,后面的工作就是运行期进行的初始化,加载,和安装插件的过程。本文主要分析初始化过程。通过官方文档:https://github.com/iqiyi/Qigsaw/blob/master/README.zh-CN.md中可以了解到,插件... 查看详情

android——qigsaw源码分析初始化过程(代码片段)

上一章节描述了Qigsaw编译期做的事情,后面的工作就是运行期进行的初始化,加载,和安装插件的过程。本文主要分析初始化过程。通过官方文档:https://github.com/iqiyi/Qigsaw/blob/master/README.zh-CN.md中可以了解到,插件... 查看详情

android——qigsaw源码分析初始化过程(代码片段)

上一章节描述了Qigsaw编译期做的事情,后面的工作就是运行期进行的初始化,加载,和安装插件的过程。本文主要分析初始化过程。通过官方文档:https://github.com/iqiyi/Qigsaw/blob/master/README.zh-CN.md中可以了解到,插件... 查看详情

android——qigsaw源码分析加载过程(代码片段)

前言在初始化流程结束之后,本章节主要是继续按执行顺序,分析加载流程。目录前言加载过程1.预加载(SplitLoadManagerImpl)1.1SplitLoadManagerImpl.loadInstalledSplitsInternal1.2SplitLoadManagerImpl.createInstalledSplitFileIntents1.3SplitLoa 查看详情

android——qigsaw源码分析加载过程(代码片段)

前言在初始化流程结束之后,本章节主要是继续按执行顺序,分析加载流程。目录前言加载过程1.预加载(SplitLoadManagerImpl)1.1SplitLoadManagerImpl.loadInstalledSplitsInternal1.2SplitLoadManagerImpl.createInstalledSplitFileIntents1.3SplitLoadManagerImpl.c... 查看详情

android——qigsaw源码分析加载过程(代码片段)

前言在初始化流程结束之后,本章节主要是继续按执行顺序,分析加载流程。目录前言加载过程1.预加载(SplitLoadManagerImpl)1.1SplitLoadManagerImpl.loadInstalledSplitsInternal1.2SplitLoadManagerImpl.createInstalledSplitFileIntents1.3SplitLoadManagerImpl.c... 查看详情

android——qigsaw源码分析安装过程(代码片段)

前言在编译,初始化,加载的准备工作都结束以后,就到了插件的安装步骤。本文按调用顺序来分析具体的安装步骤。在官方demo下,可以看到QigsawInstaller中实际执行安装的步骤:SplitInstallManager.startInstall↓SplitInstall... 查看详情

qigsaw源码之gradle插件解析(代码片段)

AndroidAppBundle为Qigsaw的前置依赖知识点。AndroidAppBundle是Android新推出的一种官方发布格式.aab,可让您以更高效的方式开发和发布应用。借助AndroidAppBundle,您可以更轻松地以更小的应用提供优质的使用体验,从而提升安... 查看详情

android插件化virtualapp源码分析(添加应用源码分析|launchpadadapter适配器|适配器添加元素|packageappdata元素)(代(代码片段)

...素,启动安装的APK应用;下图显示的RecyclerView列表如下:<android.support.v7.wid 查看详情

android8.0编译过程初步分析

Android8.0编译过程分析概述要想知道编译的过程,其实看编译的脚本,以及编译时产生的log是比较快的方法。编译相关的核心文件位于build/core下,而生成的文件在out/soong下,里面有编译过程中产生的编译相关文件... 查看详情

android启动过程activity启动源码分析(activitythread流程分析二)(代码片段)

文章目录前言一、ActivityManagerService.attachApplicationLocked二、ActivityStackSupervisor.attachApplicationLocked三、ActivityStackSupervisor.realStartActivityLocked前言在上一篇博客【Android启动过程】Activity启动源码分析(ActivityThre 查看详情

android6.0源码分析之activity启动过程

Activity最为android开发者最熟悉的组件,由ActivityManagerService服务进行调度管理,而ActivityManagerService的启动过程在activitymanagerservice服务源码分析一文中进行了详细分析,本文基于其对Activity的启动过程进行分析,同... 查看详情

android6.0phone源码分析之phone适配过程

android6.0Phone源码分析之Phone适配过程分析过Phone源码的人知道,在Phone去电过程中会调用到phone.dial()方法,而此处的Phone可以为GSMPhone或者CDMALTEPhone。对于Phone的适配,android采用了工厂模式。本文主要分析Phone的适配过程... 查看详情

鸿蒙内核源码分析(构建工具篇)|顺瓜摸藤调试鸿蒙构建过程|百篇博客分析harmonyos源码|v59.01(代码片段)

...mony<国内|国外>百篇博客系列篇.本篇为:v59.xx鸿蒙内核源码分析(构建工具篇)|顺瓜摸藤调试鸿蒙构建过程|51.c.h.o编译模块相关篇为:v58.xx鸿蒙内核源码分析(环境脚本篇)|如何防编译环境中的牛皮癣|51.c.h.ov57.xx鸿蒙内核源码分析(... 查看详情

eosio源码分析-cdt合约编译过程

智能合约代码举例纵观整体代码有如下特点符合C++代码语法,包含相应的头文件,命名空间公有继承合约基类contract在语法中出现了新的标签代码如:[eosio::contract],[eosio::action]等代码尾部出现新的宏EOSIO_DISPATCH合... 查看详情