爱奇艺重磅开源基于Android-App-Bundle动态化方案Qigsaw
Split APKs 加载
应用进程所使用到的 ClassLoader 和 Resources 均在LoadedAPK中创建。

通过 Android 9.0 LoadedAPK源码片段,我们一起了解下 Split APKs 加载过程。

通过createOrUpdateClassLoaderLocked方法名,可以知道该方法是用于创建和更新 ClassLoader。该方法有两个核心步骤。
1:如果mClassLoader为空,则创建 PathClassLoader 实例。
2:如果addedPaths不为空,则更新 PathClassLoader 实例。
该方法指明,应用进程是可以动态加载 Split APKs 代码。

通过getResources方法代码片段,可知 Split APKs 的资源路径作为mResources创建参数。
关于更多 Split APKs 加载原理细节,请阅读相关 Android 源码。
第三方应用利用 PackageInstaller 安装 split APKs 体验极其不友好,且某些国产手机对 split APKs 功能支持不完善,所以我们最终还是按照一般插件化方式安装加载 split APKs。

依据上图,如果需要动态加载 split APKs,需要解决代码、资源以及四大组件的加载。
针对 splits 代码加载,Qigsaw 采用单类加载器方式,即 base APK 和 split APKs 采用同一 ClassLoader 加载。

在 DexPathList 中,为每个 split 创建对应的Element和NativeLibraryElement实例即可。关于单类加载器更多细节,本文不再赘述,相关原理已非常成熟。
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 资源也更加方便。
Qigsaw 提供loadResources方法加载 split APKs 资源。为避免开发者写大量模板代码,Qigsaw 打包插件采用字节码操作方式自动写入该方法。
Android App Bundle 在 Manifest 文件合并过程中,会将 split APKs manifest 文件内容合并至 base APK 中。因此,所有 split APKs 四大组件信息都是已经声明在 base APK 中。
Android App Bundle 这种处理方式不支持 Manifest 更新,例如新增四大组件,所以 Qigsaw 也不支持新增四大组件。在正常开发迭代过程中,动态新增 splits 四大组件需求极少,所以 Qigsaw 与 Android App Bundle 特性保持一致。