什么是 Android 热修复(HotFix)?Tinker 和 AndFix 的原理有何不同?

解读

国内面试问“热修复”时,面试官真正想确认的是:

  1. 你是否理解线上事故必须“分钟级止血”,而无需用户主动升级;
  2. 能否把“运行时替换”这件事拆成编译期、分发期、加载期、运行期四个环节,并说出每个环节在国内合规(工信部 164 号文、个人信息保护)与性能(包大小、启动耗时、GC 抖动)上的权衡;
  3. 能否用一句话指出 Tinker 与 AndFix 的本质差异:前者走“Class 文件全量替换”,后者走“Native 方法指针替换”,并给出线上踩过的坑(小米加固、Android 13 动态加载限制、鸿蒙兼容)。

知识点

  1. 热修复定义:在进程不重启的前提下,把线上崩溃或业务缺陷以补丁形式覆盖,达到“用户无感知、后台可回滚”的目标。
  2. 国内合规红线:补丁包必须走 https 下载、签名校验、V3 签名 Scheme、不能动态下发 so 去加密个人敏感信息;应用市场备案时需声明“存在动态更新组件”。
  3. 触发时机:Application.attachBaseContext → 补丁加载必须在 ContentProvider 安装前完成,否则 Provider 中已缓存的 Class 无法回退。
  4. Tinker 原理:
    • 编译期:Gradle 插件对比新旧 APK,生成差分 dex(bsdiff)+ so(bsdiff)+ 资源(res diff,arsc 格式),合成后生成 patch.apk;
    • 加载期:单独进程(TinkerPatchService)做 dexopt,合成完整的新 dex 文件,写入 /data/data/pkg/tinker/;
    • 运行期:反射修改 PathList 的 dexElements,把新 dex 插到数组最前面,重启进程后生效;
    • 回滚:若新 dex 加载失败,删除 tinker 目录,下次走原 dex。
  5. AndFix 原理:
    • 编译期:注解处理器在需要替换的方法上打 @MethodReplace 标记,生成补丁 jar;
    • 加载期:native 层通过 dlopen 打开补丁 so,解析其中 ArtMethod 结构;
    • 运行期:利用 Android 4.4-13 不同的 ArtMethod::SetEntryPointFromJni 偏移,把旧方法入口指针直接改写为新方法入口,立即生效,无需重启;
    • 限制:只能替换非静态、非抽象、非构造方法,且对 inline、jit 优化敏感,Android 12 之后 inline-cache 导致崩溃概率升高。
  6. 性能对比:Tinker 合成 dex 过程占用 CPU 1-2 s、ROM 20-40 MB,但兼容性好;AndFix 0 ROM 增量、0 重启,但高版本 ART 结构变化大,需持续维护偏移表。
  7. 国内大厂落地:微信 Tinker 开源版 1.9.14 支持 Android 13;支付宝 AndFix 已停止维护,内部迁移至 Sophix(类 Tinker 全量替换 + AndFix 即时回退双模);美团 Robust 采用 InstantRun 插桩方案,兼容性好但包体积 +8%。

答案

Android 热修复指在用户无感知、不重新安装 APK 的前提下,通过下发补丁把线上缺陷代码替换为已修复版本,实现“分钟级止血”。

Tinker 与 AndFix 的核心差异体现在“替换粒度”与“生效时机”:

  • Tinker 采用“类全量替换”:编译期生成差分 dex,运行期在独立进程合成完整新 dex,通过反射把新 dex 插入 PathList 最前端,下次进程重启后整体生效;优点是兼容性好、可替换 so 与资源,缺点是需要一次重启、占用双倍 ROM。
  • AndFix 采用“方法指针替换”:native 层直接修改 ArtMethod 的 entry_point,把旧方法入口指向补丁中的新方法,立即生效、无需重启;优点是零 ROM 增量、用户体验最好,缺点是只能替换 Java 方法,无法处理 so 与资源,且 Android 12 以后 ART 结构变化大,官方已停止维护。

因此,国内线上环境若追求高稳定性与合规,优先采用 Tinker 类全量替换方案;若需秒级修复且方法改动小,可降级使用 AndFix 或 Sophix 双模方案,并预留回滚开关。

拓展思考

  1. Android 14 引入“restricted 更新签名”机制,补丁包若与 APK 原始签名不在同一旋转链,将被系统拒绝安装,如何改造 Tinker 的签名校验流程?
  2. 折叠屏多窗口场景下,Application 可能被系统复用而不走完整重启,如何确保补丁加载后 Activity 生命周期重新走一遍?
  3. 国内厂商加固(腾讯乐固、360 加固)会抽走 dex 并插入壳 Application,导致 Tinker 反射 PathList 失败,有哪些白名单与反射兜底策略?
  4. 工信部 164 号文要求“热更新能力需在应用市场备案”,如何在 Manifest 中声明并动态关闭热修复,以满足企业合规审计?