第1章 SplitInstall 原理与核心概念
1.1 动态交付机制
Split Install(动态交付)是 Android App Bundle 的核心能力之一,允许应用按需下载功能模块,而无需用户重新安装整个 APK。传统单体 APK 将所有代码、资源和原生库打包进一个文件,用户安装时即获取全部内容;动态交付则将应用拆分为以下组件:
| 组件 | 说明 |
|---|---|
| 基础 APK(Base APK) | 必须包含核心代码、资源和清单,所有设备安装时需要下载。 |
| 配置 APK(Configuration APK) | 根据设备屏幕密度、CPU 架构、语言等自动分发,用户无感知。 |
| 动态功能模块(Dynamic Feature Module) | 按需下载的功能单元,用户触发后从 Google Play 下载并安装。 |
发布时开发者上传 App Bundle(.aab),Google Play 根据设备配置生成优化后的 APK 分发。动态功能模块在应用运行时通过 SplitInstallManager API 发起请求,Play 服务下载对应 split APK 并合并到应用沙箱中。
关键概念:动态模块不改变清单文件
AndroidManifest.xml 中声明的 dist:fusing="true"/false 属性;fusing="true" 表示该模块可回退到基础 APK 内(减少安装包体积),false 则表示必须通过动态安装获取。
1.2 模块拆分粒度
合理拆分粒度直接影响用户体验与维护成本。推荐按业务边界或资源密集度拆分:
- 业务功能模块:例如支付、地图、AR、客服、直播、海外特性 —— 仅在用户主动进入该功能时下载。
- 条件功能模块:针对特定设备能力(如 VR 支持、大屏平板适配)或用户画像(如内测功能)使用。
- 资源包模块:大型资源文件(高清贴图、离线地图、语言包)通过
OnDemand方式下载,避免安装时一次性占用空间。
拆分过细会导致大量合成清单并增加安装器负担;拆分过粗则失去按需下载意义。推荐每个动态模块大小在 1 MB ~ 50 MB 之间,超过 150 MB 建议拆分为多个子模块或使用资产包交付(Asset Packs)。
注意:动态功能模块不能导出四大组件(Activity、Service、BroadcastReceiver、ContentProvider)的
exported="true" 于公共 Intent 过滤器,除非该模块在安装时已确定被分发。开发者应在模块清单中声明 dist:install-time=" 属性以控制安装时机。
1.3 按需下载流程
SplitInstall 的完整生命周期包括以下阶段:
- 应用检查模块是否已安装(
getSessionState+getInstalledModules())。 - 未安装则调用
SplitInstallManager.startInstall()创建一个下载请求。 - Play 服务下载对应 split APK(后台服务,不阻塞 UI)。
- 下载完成后触发安装(系统安装器管理,无需 root)。
- 应用接收
SplitInstallSessionState状态变化(Pending / Downloading / Installed / Failed)。 - 安装成功后,模块代码立即可用,亦可延迟重启 Activity 以加载新资源。
以下是一个启动下载请求的简化代码示例:
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
SplitInstallRequest request = SplitInstallRequest.newBuilder()
.addModule("chat")
.addModule("payment")
.build();
manager.startInstall(request)
.addOnFailureListener(exception -> {
// 处理失败,如网络不可用、存储不足等
})
.addOnSuccessListener(sessionId -> {
// sessionId 可用于监听下载进度
});
监听进度推荐使用 SplitInstallStateUpdatedListener:
SplitInstallStateUpdatedListener listener = state -> {
if (state.status() == SplitInstallSessionStatus.INSTALLED) {
// 模块可用,可加载组件
} else if (state.status() == SplitInstallSessionStatus.FAILED) {
// 获取错误码进行规约
int errorCode = state.errorCode();
}
};
manager.registerListener(listener);
1.4 版本兼容性
SplitInstall 要求设备环境满足以下条件:
| 要求项 | 最低版本 / 配置 |
|---|---|
| Android 系统版本 | API 21 (Android 5.0) 及以上 |
| Google Play 服务 | Play 服务 16.0.0 以上(通常随 Google Play 商店自动更新) |
| Google Play 商店 | 必须启用(非 Play 商店发行渠道不支持动态安装) |
| 开发工具 | Android Studio 3.2+,Gradle 4.6+,Android Gradle Plugin 3.2+ |
| 发布格式 | 应用必须以 App Bundle (.aab) 形式上传到 Google Play 控制台 |
兼容性提示:在模拟器或未安装 Google Play 商店的测试设备上,动态功能模块下载会立即失败(错误码
SplitInstallErrorCode.PLAY_STORE_NOT_FOUND)。在开发阶段可使用 adb shell am start-foreground-service 配合本地测试 APK 拆分工具模拟下载,但实际安装仍需 Play 服务参与。
1.5 动态交付核心流程示意图
1.6 构建配置示例
在模块级 build.gradle 中声明动态功能模块(以 feature_chat 为例):
// 基础模块(app/build.gradle)
android {
dynamicFeatures = [':feature_chat', ':feature_payment']
}
// 动态功能模块(feature_chat/build.gradle)
plugins {
id 'com.android.dynamic-feature'
id 'kotlin-android'
}
android {
compileSdk 34
defaultConfig {
minSdk 21
}
}
dependencies {
implementation project(':app')
// 其他依赖
}
动态模块的清单文件需包含 dist:module 标签:
<dist:module
dist:instant="false"
dist:title="@string/feature_chat_title">
<dist:fusing dist:include="true" />
</dist:module>
1.7 常见错误与调试
- PLAY_STORE_NOT_FOUND (1):设备缺少 Google Play 商店,需在真机或模拟器(带 Play 服务)上测试。
- NETWORK_ERROR (3):下载期间网络断开,应用应监听状态并自动重试。
- ACCESS_DENIED (4):请求的模块在 Play 控制台未上架或未关联至当前版本,需检查控制台“高级设置”中的模块分发。
- ACTIVE_SESSIONS_LIMIT_EXCEEDED (5):同时启动的安装会话超过 5 个,建议串行或使用队列。
- API_NOT_AVAILABLE (6):当前系统或 Play 版本不支持 SplitInstall API,检查设备兼容性。
调试建议:在 Android Studio 中使用 Build > Build Bundle(s) / APK(s) > Build APK(s) 生成多 APK 测试包,然后通过
adb install-multiple *.apk 手动安装模拟基础 + 功能模块,即使无 Play 商店也能验证模块代码加载路径。