Qigsaw原理

Qigsaw借助Android App Bundle开发套件完成dynamic feature的打包,大大降低Qigsaw开发维护成本。因此Qigsaw关心的重点落在如果安装加载dynamic feature生成apk上。

第三方应用利用PackageInstaller安装split APKs体验极其不友好,且某些国产手机对split APKs功能支持不完善,所以我们最终还是按照一般插件化方式安装加载split APKs。

apk_fomat

依据上图,如果需要动态加载split APKs,需要解决代码、资源以及四大组件的加载。

Split APKs代码加载

针对splits代码加载,Qigsaw采用单类加载器方式,即base APK和split APKs采用同一ClassLoader加载。

split_load_code

在DexPathList中,为每个split创建对应的ElementNativeLibraryElement实例即可。关于单类加载器更多细节,本文不再赘述,相关原理已非常成熟。

Split APKs资源加载。

Splits资源加载相较于代码加载会复杂,因为不同系统版本或不同手机厂商都会存在一些兼容性问题。

split_load_resources

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资源如何加载呢?我们来看一段代码。

split_load_resources_sample

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类似。首先,通过一张图来了解。

qigsaw_core_library_flow

在爱奇艺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。