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 动态交付完整流程

从用户点击「加载功能」到模块就位,经历以下步骤:

  1. 应用调用 startInstall → 构造 SplitInstallRequest 并传入模块列表。
  2. Play 商店接收请求 → 校验模块是否存在、版本兼容、设备存储空间。
  3. 开始下载 → 分片拉取 .apks(打包后的模块),触发监听状态为 DOWNLOADING。
  4. 下载完成 → 解压与安装 → 系统将模块集成到 App 的 split APK 目录,状态变为 INSTALLING。
  5. 安装完成 → 通知应用 → 状态变为 INSTALLED,模块代码和资源可立即使用。
  6. 错误处理 → 任何阶段失败都会触发 FAILED,应用需根据 errorCode 决定重试或降级。
SplitInstall 动态交付完整流程 应用调用 startInstall Play 商店校验与调度 分片下载 (DOWNLOADING) 解压与安装 (INSTALLING) 安装完成 (INSTALLED) 失败 (FAILED) 每个阶段均可触发 FAILED,需监听处理 最大并发 10 session,单次最多 5 模块

1.6 模块分发机制要点

  • 模块命名: 使用 com.android.dynamic.feature 插件,在 build.gradle 的 dist 闭包中声明分发类型。
  • 版本一致性: 动态模块必须与基础 APK 使用相同的 versionCode,且签名一致。
  • 安装目录: 模块安装在 /data/app/.../split_...apk,通过 PackageManager 的 getPackageInfo 可见。
  • 清理策略: 调用 deferredUninstall 可标记模块在空闲时删除;直接调用 cancelInstall(sessionId) 取消进行中的 session。
性能优化建议:
- 使用 SplitInstallSessionState.bytesDownloaded 更新 UI 进度条,避免频繁刷新主线程。
- 将模块下载监听注册在 Application.onCreate 中,确保重启后 session 恢复。
- 对大型模块(>200MB)启用 SplitInstallRequest.setUseFastInstall() 以加速安装。
- 通过 SplitInstallManager.getInstalledModules() 缓存模块状态,减少重复请求。