Android 热修复简述

728 阅读3分钟

Android 热修复就像给应用打 “补丁”,不用重新安装就能修复线上 bug。下面用通俗语言拆解核心原理和主流方案:

一、热修复为啥重要?

App 发布后发现 bug,传统方式要用户重新下载安装,热修复则能像 “打补丁” 一样,直接推送修复包,用户无感知解决问题。比如微信发现支付页 bug,直接推补丁修复,不用强制更新 App。

二、三大核心方案:换类、改方法、改执行地址

1. 类替换方案(代表:Tinker)

  • 核心思路:把修复后的类打包成新 Dex,插入到类加载器的最前面,让系统优先加载新类。
    类比:就像你有一本旧书(原 App),某一页写错了,热修复给你一张正确的纸(新 Dex),夹在书的最前面,看书时先看这张纸,旧内容被覆盖。

  • 关键问题

    • AOT 编译坑:系统会把常用代码编译成机器码(类似把书里的重点内容抄到笔记本),若旧代码被编译,新类可能不生效。Tinker 的解决办法是直接替换整个 Dex,强制用新代码。
    • 性能影响:替换后需要重新编译新 Dex,可能导致启动变慢。

2. 编译时方法替换(代表:Robust)

  • 核心思路:编译时在每个方法前加一个 “路口”,判断是否走补丁逻辑。
    类比:在每个函数门口装个 “安检”,如果发现需要修复,就引导到补丁代码,否则走原逻辑。

  • 实现细节

    • 编译时给每个方法插入代码:if (补丁存在,走补丁逻辑 else 走原逻辑
    • 补丁包只包含修复的方法逻辑,通过 “桥接” 类告诉宿主该走哪个补丁。
  • 优点:兼容性好,不修改系统;缺点是会增加包体积(每个方法都加了 “安检”)。

3. 运行时方法替换(代表:Sophix/AndFix)

  • 核心思路:直接修改虚拟机中方法的执行地址,让旧方法指向新逻辑。
    类比:原方法是 “旧电话号码”,热修复把它改成 “新电话号码”,调用时直接拨新号。

  • 技术难点

    • 需要操作虚拟机底层的 ArtMethod 结构,不同 Android 版本结构可能不同,兼容性差(比如厂商定制系统可能改了结构)。
    • Android 8.0 后用 JVM TI 接口(类似官方提供的修改工具),更稳定。

三、So 库修复:替换动态库

  • 原理:和类替换类似,把修复后的 So 库插入到加载路径最前面,系统优先加载新 So。
  • 难点:So 库可能依赖其他 So(如 a.so 依赖 b.so),需要按顺序提前加载补丁 So,避免依赖失败。

四、各方案对比与典型场景

方案核心优势缺点适用场景
类替换(Tinker)覆盖范围广,适合大改动启动性能有损耗修复大量类或结构变化的 bug
编译时插桩(Robust)兼容性好,无系统 Hook包体积增加,性能略降小范围方法修复,追求稳定性
运行时替换(Sophix)修复即时生效兼容性差,需适配多版本紧急修复,要求立即生效的场景

五、热修复的 “坑” 与解决思路

  1. AOT/JIT 编译影响:系统提前编译的旧代码可能没被替换,需强制 “去优化” 让代码重新解释执行。
  2. 资源匹配问题:补丁中的资源 ID 可能和宿主冲突,需特殊处理资源加载逻辑。
  3. 兼容性挑战:不同手机厂商的系统底层实现不同,需大量适配(如 ArtMethod 结构差异)。

六、一句话总结

热修复是一场 “与虚拟机的博弈”:通过替换类、修改方法执行路径或动态库,让应用在不重启的情况下运行新逻辑。不同方案各有优劣,实际应用中需根据场景选择,或结合多种方案提升成功率。