aab方式介绍
一种发布格式,将所有经过编译的代码和资源包含在一个构件工件中;
是经过签名的二进制文件,可将应用的代码和资源组织到模块中;
简单来说, android插件化是 分为 base apk 和feature apk;
base 为基础包; feature为单个模块包, 可通过cdn网上动态下载 ,动态添加需要的功能模块;
组件化和插件化的区别:
同:
- 两者都可以使基础包和模块包作为一个app运行,便于开发;
- 充分解耦;
异:
- 组件化还是作为统一的app,只是开发时作为单独的app;
- 插件化所有模块就是单独的app,但单个模块不能安装,由基础包统一调动;
apk拆分
- 基本apk: 包含所有其他apk都可以访问的代码和资源,并提供应用的基本功能;
- 配置apk: 每个配置apk都包含针对特定屏幕密度,cpu架构或语言的原生库和资源;用户只会下载与设备对应的apk;
- 功能模块apk: 包含模块化处理的功能的代码和资源,
- 安装时分发
- 按需分发
- 根据条件提供
- 免安装分发
Qigsaw 插件化介绍
原地址文档
爱奇艺提供的 一套基于 Android App Bundle(AAB) 的动态化方案;
特点:
- 单base apk + 多个Split apk;
- apk 安装后,可以按需请求下发或者更新模块apk;
- api 21(5.0) 以上使用,低版本下发完整apk;
优势:
- 使用aab原生的开发套件 google 的 play core library 公开接口实现,支持所有aab的功能特性;
- 任何进程均可动态加载插件,支持四大组件;
- 可无缝切换aab方案
- 仅一处hook(android9.0+无需hook),少量私有api;
方案:
- 基线工程和插件工程独立开发,基线工程提供基础sdk供插件工程编译使用,避免类重复;
- 插件化方案一般将插件app 运行在 :plugin 插件进程,用ipc方案进程间通信;
当项目编译完成后,可通过adb install-multiple
命令将baseapk和插件apk安装到手机中;
qigsaw打包插件支持内置插件,所有内置插件都会拷贝到base apk 的assets
目录; 非内置插件,qigsaw打包插件会将其上传到cdn服务器,解决业务方后顾之忧;
apk 安装命令
aab安装或者defalutApk安装都是通过adb install-multiple
命令来安装baseapk和splitapks;
- 多apk安装,必须保证baseapk存在; 如 base-master.apk、base-xxhdpi、base-zh.apk 三个apk的安装;
- 已安装baseapk,继续安装splitapk,添加 -p参数(partial application install) 部分安装, 后跟baseapk包名;
apk update 步骤
ondemand =true表示按需分发 即不会将插件模块打进主apk中 而是上传到服务端 随后在apk安装之后进行下发 不过目前插件化模块的上传逻辑我们并没有实现 所以其实只能支持设置ondemand =false
- 创建插件更新分支,修改代码;
- 修改插件版本号;
- 配置qigsawSplit中的版本号;
- splitInfoVersion
- 配置mapping
- 配置old apk
- 再次执行qigsawAssemble${variantName}
Qigsaw 源码分析
前言:
Qigsaw结构主要分为:
- 编译期的Aop ; 由QigsawPlugin实现,主要是经transform api 处理四大组件的Resource对象和生成统计Split模块中四大组件等信息的ComponentInfo类,涉及到的主要技术点关键词为:ASM ,TransformApi, gradleTasks;
- 运行期插件的获取和激活; 主要分为split的安装和加载过程,涉及到的主要技术点关键词为:Binder通信,ClassLoader,loadDex;
Qigsaw plugin 源码分析
QigsawAppBasePlugin app模块应用的插件
注册的transform
- SplitComponentTransform (taskName:processSplitComponent) 处理split manifest文件, 生成class文件记录除provider的信息,生成代理provider
- 通过模块的copySplitManifestTask 在split模块的Manifest生成后会将xml复制到临时目录;
- 读取临时目录中所有split的xml,获取activity,service,receiver,provider和application的名称,建立Map添加key为featureName和各组件大写后缀名,value为各组件名称的键值对;
- Asm创建
com.iqiyi.android.qigsaw.core.extension.ComponentInfo
类,遍历Map,Asm写入属性- application: 创建常量属性
- provider:构建
providerName + _Decorated_ + splitName
作为代理provider的名称,asm创建代理Provider继承自com.iqiyi.android.qigsaw.core.splitload.SplitContentProvider
;此代理provider会在AABExtension
激活split时处理; - 其他: 创建
,
分隔的常量属性
- SplitResourcesLoaderTransform (taskName: splitResourcesLoader) 主要是在组件中插入 SplitInstallHelper#loadResources}方法,此处transform处理jar和Directory的字节码;
- 如果是基线包
- 获取插件扩展参数的act,对参数中指定activity使用
SplitActivityWeaver
进行方法的注入操作:- SplitActivityWeaver: 访问activity的所有方法,在
getResources
方法中(注意:没有此方法则新建插入,有则直接插入 )注入SplitInstallHelper#loadResources
方法,即通过SplitCompatResourcesLoader#loadResources加载插件资源;
- SplitActivityWeaver: 访问activity的所有方法,在
- 获取插件扩展参数的act,对参数中指定activity使用
- 如果是插件包
- 获取split的mergedManifest文件,读取activities,services,receivers,分别使用各自的Weaver 进行编织:
- SplitServiceWeaver: 访问service的所有方法,在
onCreate
方法中注入SplitInstallHelper#loadResources
方法 - SplitReceiverWeaver: 访问receiver的所有方法,在
onReceive
方法中注入SplitInstallHelper#loadResources
方法
- SplitServiceWeaver: 访问service的所有方法,在
- 获取split的mergedManifest文件,读取activities,services,receivers,分别使用各自的Weaver 进行编织:
- 如果是基线包
主要的tasks
- ExtractTargetFilesFromOldApk : 如果插件参数中设置oldApk,则复制
assets/qigsaw/**
目录文件到临时目录; - GenerateQigsawConfig: 构建 QigsawConfig.java 文件, 注意如果指定了oldApk,qigsawId即为oldApk中的qigsawId;
- Split:copySplitManifestTask: 取split合并后的manifest,复制到app的临时目录;
- Split:ProcessSplitApkTask :在split的assembleTask运行后,处理生成的splitApk,按照abi收集apkData信息,支持单个abi打包,最终会生成一个签名后的apk包和单个split.json记录split的信息,默认的apkData.url为
asset://
开头,buildIn标记(qigsawAssembleTask中用于判断是否上传至cdn中的标记) 由manifest中onDemand和插件参数releaseSplitApk 决定boolean builtIn = !onDemand || !releaseSplitApk
; - QigsawProcessManifestTask: 将app中merge/bundleManifest 的provider和splitManifest中的代理Provider 做同名处理,将同名的appProvider名称修改为
providerName + _Decorated_ + splitName
在写回,用于启动时AABExtension的解析; - CreateSplitDetailsFileTask :即qigsawAssembleTask,真正的打基线包的Task
- 先获取所有split生成的json信息
- 生成SplitDetails信息,收集app的所有打包信息
- 如果设置了oldApk,会判断是否是更新态,如果不是更新态直接使用oldApk中的SplitDetails信息;
- 如果是更新态,获取oldApk 的qigsaw.json信息,判断如果当前打的包moduleVersion(versionName@versionCode)和oldApk中的同一splitModule的version是否相同,如果split版本变化意为split的版本已经更新,标记当前更新状态,添加当前splitName到updateSplits字段中,创建SplitDetails对象;
- 如果当前的split的buildIn参数为false,尝试上传操作,上传成功指定apkData.url 为
http://
开头数据,表示在qigsaw安装阶段会从本地的asset目录获取apk还是从网络路径获取apk;
- 根据依赖关系重排序splitInfo 列表,根据SplitDetail写出qigsaw.json文件,获取当前abi列表数据并写出baseAppCpuAbiListFile文件,此文件也会复制到asset目录;
- 当前split的buildIn为true,如果是更新状态则将每个split的已签名的apk复制到即将打包的asset目录中,否则直接复制oldApk中的apk;
- SplitBaseApkForABIsTask:packageAppTask之后运行,用于根据baseAppCpuAbiListFile文件缩减到指定的abi的再输出apk;
- QigsawProguardConfigTask: 额外添加qigsaw的混淆和接收插件参数mapping文件保持混淆的一致性;
QigsawDynamicFeaturePlugin feature模块应用的插件
FeaturePlugin 注册 SplitResourcesLoaderTransform 和 SplitLibraryLoade