Skip to content

dynamic-delivery-hot-update ​

1. 实验目标 ​

用最小骨架演示三类「动态化 / 热更新」机制(对应 docs/05-bridge-hybrid/08):

  • Android App Bundle / Dynamic Feature:用 Play Core 按需拉取 feature 模块
  • ClassLoader 热更新(Tinker/Instant Run 思路):在 BaseDexClassLoader 的 dexElements 前插入补丁 dex,使补丁类优先加载
  • React Native / CodePush 思路:JS bundle 远程下发、运行时 reload(本实验给出加载顺序骨架,不引入 RN 依赖)

本实验以骨架代码 + 注释形式给出(见「7. 关键源码」);完整可运行的 Dynamic Feature 需要 Android App Bundle 工程,暂不接入 Android Host 构建。

2. 工程要点 ​

  • Dynamic Feature:在 build.gradle 声明 apply plugin: 'com.android.dynamic-feature',主工程 android.dynamicFeatures += [':feature'];运行时 SplitInstallManager 请求安装
  • 类加载热更新:反射拿到 BaseDexClassLoader.pathList.dexElements,将补丁 dex 的 dexElements 拼到数组最前面(双亲委派下优先命中补丁)
  • 资源/so 同理需单独处理;补丁需签名校验,否则成投毒入口

3. 运行方式 ​

bash
# 1) Dynamic Feature(需 bundletool)
./gradlew :app:assembleDebug
bundletool build-apks --bundle app.aab --output app.apks
bundletools install-apks --apks app.apks

# 2) 类加载热更新:直接运行「7. 关键源码」中的 hotfixClassLoader 演示(JVM 环境即可验证加载顺序)

4. 预期现象 ​

  • Dynamic Feature:首次进入相关功能时触发下载,下载完成后才能进入(按需加载)
  • 类加载热更新:打补丁后,反射查询 dexElements 顺序,补丁 dex 位于数组首位 → 补丁类被优先解析

5. 常见误区 ​

误区实际情况
热更新能改任何代码不能改 <application>、不能改 AndroidManifest、不能改 SO 加载方式(有平台限制与合规风险)
补丁直接覆盖类即可必须插到 dexElements 前面,且要处理资源/so/签名,否则崩溃或无效
Dynamic Feature = 热更新前者是「按需安装」(需商店),后者是「不发包修 bug」,机制与合规边界不同

6. 对应知识库文档 ​

7. 关键源码 ​

kotlin
// 类加载热更新:将补丁 dex 插到 pathList.dexElements 最前面(示意)
fun installHotfixPatch(classLoader: BaseDexClassLoader, patchDex: File) {
    val pathList = classLoader.javaClass.getDeclaredField("pathList").apply { isAccessible = true }.get(classLoader)
    val dexElementsField = pathList.javaClass.getDeclaredField("dexElements").apply { isAccessible = true }
    val oldElements = dexElementsField.get(pathList) as Array<Any>

    val makeDexElements = pathList.javaClass.getDeclaredMethod(
        "makeDexElements", List::class.java, File::class.java, List::class.java, ClassLoader::class.java
    ).apply { isAccessible = true }
    val patchElement = (makeDexElements.invoke(
        pathList, listOf(patchDex), patchDex.parentFile, mutableListOf<IOException>(), classLoader
    ) as Array<Any>)[0]

    val newElements = Array(oldElements.size + 1) { i ->
        if (i == 0) patchElement else oldElements[i - 1]   // 补丁置顶:优先命中
    }
    dexElementsField.set(pathList, newElements)
}

接入状态 ​

📋 文档化(未接入宿主编译) :真·Dynamic Feature(com.android.dynamic-feature feature module)或 Tinker 热修需要额外模块/补丁构建链,过重;本 README 保留 Play Core 拉取与 Dex 插桩骨架供对照,未生成可运行的宿主 App 入口。

站点构建时间:2026/8/24 23:43:17