1. SplitInstall 基础原理与模块分发机制
1.1 动态交付概述
Android 动态交付(Dynamic Delivery)允许将应用拆分为多个按需或条件性分发的功能模块。通过 SplitInstall API,应用可以在运行时请求、下载并安装这些模块,从而缩小初始安装包大小、降低首装时长,同时支持模块化演进。
核心收益: 按需加载 → 减少用户下载体积;条件分发 → 针对设备特性(如语言、架构)只推送必要模块;即时生效 → 无需用户手动更新。
1.2 模块拆分策略
动态交付支持三种模块类型,其定义与分发规则如下:
| 模块类型 | 分发方式 | 典型场景 | 安装默认行为 |
|---|---|---|---|
| On-demand(按需) | 用户触发请求 → 运行时下载 | 高级功能、游戏皮肤、分析 SDK | 初始不安装 |
| Conditional(条件) | 依据设备属性自动分发 | 语言包、屏幕密度资源、ABI 资源 | 匹配条件时自动包含 |
| Instant(即时) | 即时体验应用时加载 | Google Play Instant 不完整功能 | 仅即时运行环境有效 |
模块拆分应遵循 单一职责原则:每个模块只封装一个逻辑功能域,避免交叉依赖。常见实践是将功能模块、语言模型库、机器学习模型文件、UI 组件库等拆分为独立动态模块。
1.3 请求规则与 API 调用
运行时通过 SplitInstallManager 发起请求。核心 API 方法及行为如下:
1.3.1 创建请求
// Kotlin 示例
val manager = SplitInstallManagerFactory.create(context)
val request = SplitInstallRequest.newBuilder()
.addModule("feature_onboarding")
.addModule("feature_ar_camera")
.build()
manager.startInstall(request)
请求规则:
- 一次最多请求 5 个模块(Google Play 限制)。
- 已安装的模块再次请求会直接返回成功状态。
- 安装进行中时重复请求会触发
SplitInstallException(错误码SESSION_NOT_FOUND或INCOMPATIBLE_WITH_EXISTING_SESSION)。 - 模块依赖关系需在构建时声明(通过
dist:dist或feature插件),运行时无依赖解析。
1.3.2 请求与下载生命周期
SplitInstallManager 提供 startInstall 和 deferredInstall 两种启动方式:
- startInstall → 立即调度下载流程,返回
SplitInstallSessionState对象。 - deferredInstall → 将安装延后到后台静默执行,适合非紧急模块。
1.4 下载生命周期详解
每个模块的下载过程由 5 个阶段串联组成。通过注册 SplitInstallStateUpdatedListener 可以监听状态变化:
val listener = SplitInstallStateUpdatedListener { state ->
when (state.status()) {
SplitInstallSessionStatus.PENDING -> // 等待网络或资源
SplitInstallSessionStatus.DOWNLOADING -> {
// bytesDownloaded / totalBytesToDownload
}
SplitInstallSessionStatus.INSTALLING -> // 解压与验证
SplitInstallSessionStatus.INSTALLED -> // 模块可用
SplitInstallSessionStatus.FAILED -> {
// 获取 SplitInstallErrorCode
}
}
}
manager.registerListener(listener)
| 阶段 | 状态常量 | 关键行为 | 错误处理建议 |
|---|---|---|---|
| 待处理 | PENDING | 排队等待网络、磁盘空间、Play 商店确认 | 检查 NetworkStatus 与设备存储 |
| 下载中 | DOWNLOADING | 分片下载,可获取进度百分比 | 断点续传(Play 原生支持) |
| 安装中 | INSTALLING | 拆包、校验签名、复制文件 | 避免进程被杀,可重试 session |
| 已安装 | INSTALLED | 模块资源与代码可用 | 无需额外处理 |
| 失败 | FAILED | 返回 SplitInstallErrorCode | 按错误码分类重试 |
常见错误码(SplitInstallErrorCode):
-
-
-
-
-
-
INCOMPATIBLE_WITH_EXISTING_SESSION:重复请求。-
SESSION_NOT_FOUND:session 过期或被清理。-
NETWORK_ERROR:网络不可用或超时。-
INSUFFICIENT_STORAGE:磁盘不足。-
ACTIVE_SESSIONS_LIMIT_EXCEEDED:超出并发限制(最大 10 个 session)。
1.5 动态交付完整流程
从用户点击「加载功能」到模块就位,经历以下步骤:
- 应用调用 startInstall → 构造
SplitInstallRequest并传入模块列表。 - Play 商店接收请求 → 校验模块是否存在、版本兼容、设备存储空间。
- 开始下载 → 分片拉取 .apks(打包后的模块),触发监听状态为
DOWNLOADING。 - 下载完成 → 解压与安装 → 系统将模块集成到 App 的 split APK 目录,状态变为
INSTALLING。 - 安装完成 → 通知应用 → 状态变为
INSTALLED,模块代码和资源可立即使用。 - 错误处理 → 任何阶段失败都会触发
FAILED,应用需根据 errorCode 决定重试或降级。
1.6 模块分发机制要点
- 模块命名: 使用
com.android.dynamic.feature插件,在build.gradle的dist闭包中声明分发类型。 - 版本一致性: 动态模块必须与基础 APK 使用相同的
versionCode,且签名一致。 - 安装目录: 模块安装在
/data/app/.../split_...apk,通过PackageManager的getPackageInfo可见。 - 清理策略: 调用
deferredUninstall可标记模块在空闲时删除;直接调用cancelInstall(sessionId)取消进行中的 session。
性能优化建议:
- 使用
- 将模块下载监听注册在
- 对大型模块(>200MB)启用
- 通过
- 使用
SplitInstallSessionState.bytesDownloaded 更新 UI 进度条,避免频繁刷新主线程。- 将模块下载监听注册在
Application.onCreate 中,确保重启后 session 恢复。- 对大型模块(>200MB)启用
SplitInstallRequest.setUseFastInstall() 以加速安装。- 通过
SplitInstallManager.getInstalledModules() 缓存模块状态,减少重复请求。