1. Qigsaw插件框架详解
Qigsaw是爱奇艺自主研发的动态化框架,其核心优势如下:
- 利用Android App Bundle开发套件,极速开发体验。
- 支持Android App Bundle所有功能特性,"山寨"Play Core Library公开接口实现,开发者阅读官方文档即可愉快开发。
- 任何进程均可动态加载插件,支持Android四大组件动态加载。
- 如果您的应用有出海需求,可无缝切换至Android App Bundle方案。
- 仅一处Hook,少量私有API访问,保证框架稳定性。
Android动态化方案,在国内已蓬勃发展数年之久,其核心目的是减少应用包体积,提升应用安装率。Google在减少应用包体积上的探索也从未停息,下面我们一起来看看Google在这方面的努力。
2. Google减少应用包体积方案演进
3. Multiple APK
上图中生成的产物,通过文件名我们可以很清楚知道该APK作用于何种配置的设备。Android设备的多样性,导致Multiple APK并未朝着Google期待的方向发展。因为您有可能为每个版本构建数百个APKs,大大降低迭代效率。国外开发者对此也并不感冒,这也成为Google的一块心病。
4. Android App Bundle
Android App Bundle是一种全新的应用上传格式(.aab),它包含所有编译代码和资源。当您上传aab文件至Google Play后,Google Play将aab文件拆分成一系列APKs并签名。
此外,您也可以在应用项目中添加dynamic feature模块,这些模块并不需要在应用首次安装时一起被下载安装。您可以通过使用Play Core Libray在应用运行过程中动态安装dynamic feature。dynamic feature类似国内插件化提供的能力,但dynamic feature功能更强大。
通过上图,可以看到dynamic feature可以基于设备配置选取对应的Configuration Split APKs,如此可以进一步减小dynamic feature安装包体积。
更多关于Android App Bundle细节,请阅读官方文档,本文不再赘述。Android App Bundle之所以能够支持应用运行期间安装dynamic feature,得益于Android 5.0推出的Split APKs功能。
5. Split APKs
Split APKs是Android 5.0引入的一种全新应用安装机制,其目的是为解决APK体积日益增大问题。Split APK可以将一个完整庞大的APK按照CPU架构、屏幕密度等维度拆分成多个独立APKs。当应用APK下载更新时,依据当前设备配置选取对应配置APKs安装即可。
Android 5.0之前,一个APK代表一个应用。在Split APKs问世之后,一个应用可能对应多个APKs。所有Split APKs拥有相同包名和签名。
Android提供两种方式安装Split APKs。
- adb install-multiple [base-apk, split1-apk]
- PackageInstaller.
应用进程所使用到的ClassLoader和Resources均在LoadedAPK中创建。
通过Android 9.0 LoadedAPK源码片段,我们一起了解下Split APKs加载过程。
5.1 ClassLoader创建
通过createOrUpdateClassLoaderLocked方法名,可以知道该方法是用于创建和更新ClassLoader。该方法有两个核心步骤。
- 如果
mClassLoader为空,则创建PathClassLoader实例。 - 如果
addedPaths不为空,则更新PathClassLoader实例。
该方法指明,应用进程是可以动态加载Split APKs代码。
5.2 Resources创建
通过getResources方法代码片段,可知Split APKs的资源路径作为mResources创建参数。
关于更多Split APKs加载原理细节,请阅读相关Android源码。
5.3 Play Core Library
文章开始介绍Qigsaw核心优势有提到,Qigsaw"山寨"Play Core Library公开接口实现,开发者阅读其官方文档即可开发。因此,在此主要介绍下Play Core Library工作流程。
当爱奇艺App在运行过程中,用户需要使用游戏插件,会经历以下过程。
- 爱奇艺App通过Play Core Library发起游戏APK安装请求。
- 当Google Play收到请求后,首先请求游戏APK相关数据信息,请求成功后开始下载并安装游戏APK。
- 在请求、下载以及安装整个过程中,Google Play会将整个过程所有状态返回给爱奇艺App,包括请求结果、下载进度、安装结果等。
- 当安装完成以后,爱奇艺App就可以使用游戏APK。
在Android 7.0版本之前,当Split APK安装完成之后,应用无法立即使用Split APK。因此Play Core Library提供SplitCompat模式让App可立即使用Split APK。
5.4 Qigsaw开发体验
在开发阶段,开发者使用Android App Bundle原生开发套件即可开发调试Split APKs。
Android App Bundle为dynamic feature提供全新插件com.android.dynamic-feature,它的编译产物是.apk文件。当您的项目编译完成后,Android Studio通过命令adb install-multiple命令将base apk和split apks安装至您的手机。如果您的开发手机系统版本低于5.0,则会依据当前手机设备组装成一个完整apk文件安装至该手机。
在发布阶段,Qigsaw提供打包插件让开发者享受一条龙服务,开发者不必关心dynamic feature的上传分发。
Qigsaw打包插件支持内置dynamic feature,所有内置dynamic feature都会被拷贝至base apk的assets目录。对于非内置dynamic feature,Qigsaw打包插件会将其上传至CDN服务器,解决业务方后顾之忧。
5.5 Qigsaw原理
Qigsaw借助Android App Bundle开发套件完成dynamic feature的打包,大大降低Qigsaw开发维护成本。因此Qigsaw关心的重点落在如果安装加载dynamic feature生成apk上。第三方应用利用PackageInstaller安装split APKs体验极其不友好,且某些国产手机对split APKs功能支持不完善,所以我们最终还是按照一般插件化方式安装加载split APKs。
依据上图,如果需要动态加载split APKs,需要解决代码、资源以及四大组件的加载。
-
Split APKs代码加载
针对splits代码加载,Qigsaw采用单类加载器方式,即base APK和split APKs采用同一ClassLoader加载。
在DexPathList中,为每个split创建对应的Element和NativeLibraryElement实例即可。关于单类加载器更多细节,本文不再赘述,相关原理已非常成熟。
-
Split APKs资源加载
Splits资源加载相较于代码加载会复杂,因为不同系统版本或不同手机厂商都会存在一些兼容性问题。
Android Gradle Plugin在资源打包时,会对res目录下资源文件分配一个唯一Id。
Id前两位PP为Package Id,代表应用类型。是系统应用、第三方应用、Instant App或Dynamic Feature等。
Id中间两位TT为Type,代表资源类型。是drawable、layout或string等。
Id后四位EE为Entry,代表该资源顺序。
所有第三方应用base APK资源Package Id均为7F,Android App Bundle对splits资源打包时会基于7F依次递减分配Package Id。因此,即使我们将split APKs资源添加到当前应用Resources实例中,也不会出现资源冲突问题,splits访问base资源也更加方便。 Instant Apps资源打包是基于7F依次递增。
通过Android App Bundle解决splits资源打包问题,那么splits资源如何加载呢?我们来看一段代码。
Qigsaw提供loadResources方法加载split APKs资源。为避免开发者写大量模板代码,Qigsaw打包插件采用字节码操作方式自动写入该方法。
-
Split APKs四大组件加载
Android App Bundle在Manifest文件合并过程中,会将split APKs manifest文件内容合并至base APK中。因此,所有split APKs四大组件信息都是已经声明在base APK中。Android App Bundle这种处理方式不支持Manifest更新,例如新增四大组件,所以Qigsaw也不支持新增四大组件。在正常开发迭代过程中,动态新增splits四大组件需求极少,所以Qigsaw与Android App Bundle特性保持一致。
-
Split APKs安装过程
Play Core Library是如何安装、加载split APKs,Qigsaw安装、加载split APKs与Play Core Library类似。首先,通过一张图来了解。
在爱奇艺App运行过程中,当X进程发起安装游戏APK请求时,会经历以下步骤。
X进程通过Qigsaw Core Library发起游戏APK安装请求。- 当主进程收到请求后,开始下载并安装游戏APK。
- 在下载、安装整个过程中,Qigsaw Core Library会将整个过程所有状态返回给爱奇艺App,包括下载进度、安装结果等。
- 当安装完成以后,爱奇艺App就可以使用游戏APK。
Qigsaw下载、安装split APKs均在主进程处理,split APKs的加载则发生在X进程。Qigsaw安装、加载split APKs原则是,哪个进程发起split APKs安装请求,就在哪个进程加载split APKs。
-
Qigsaw拓展功能
在实际开发过程中,Android App Bundle所支持的功能特性并不满足我们需求。因此,Qigsaw在Android App Bundle基础上拓展了几个功能。
- Split APKs的Application初始化。
- Split APKs的Content Provider动态加载。
- 多进程支持。
- 通过Tinker patch完成split APKs热更新。
在此,我们首先介绍Qigsaw多进程功能。以下图场景为例。