Administrator
发布于 2026-08-25 / 29 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 53 篇:热修复原理与实现:从 Tinker 到 Sophix 的技术演进

上一篇讲了 ASM 字节码插桩,这篇是它的实战应用——热修复。线上 App 出了 bug,难道只能等下个版本?热修复让用户无感知就修好。理解它的原理能让你对 Android 类加载、dex 机制有全新认识。

什么是热修复

线上 App 出现紧急 bug(崩溃、白屏、严重逻辑错误)时,无需发版,用户下次启动就能加载到修复后的代码。这背后的技术:

流派

代表方案

核心思路

资源替换

Tinker

下发 patch.dex,重启生效

类替换

AndFix/Sophix

Native hook 替换方法

即时编译

Robust

编译期给每个方法插桩

ART Profile

Play Feature Delivery

模块化下发

类加载机制:修复的前提

Android 用 PathClassLoader 加载 APK 里的 dex,链路是:

PathClassLoader
  └─ DexClassLoader (base/classes.dex)
       └─ DexClassLoader (patch.dex, 优先级最高)

如果 patch.dex 里有一个和 classes.dex 同名同包的类,patch 里的版本会被优先加载。这就是 Tinker 的基础:用户的 bug 类只在 patch.dex 里,全量替换。

Tinker:全量替换派的代表

Tinker(微信)是开源热修复方案的事实标准。原理:

  1. 客户端运行 base APK(有 bug 的版本)

  2. 服务器下发 patch(差异计算出来的差分包)

  3. 客户端合并 patch 到 base.dex,生成新的 classes.dex

  4. 重启后,新 dex 取代旧 dex

Patch 的生成:bsdiff 算法对 base.dex 和 new.dex 做二进制差量,平均只有几 MB。

接入 Tinker:

// app/build.gradle
plugins {
    id("com.tencent.tinker") version "1.9.14.4"
}

dependencies {
    implementation("com.tencent.tinker:tinker-android:1.9.14.4")
}

// 自定义 Application
class MyApplication : TinkerApplication(
    1, // tinkerFlags
    "com.example.MyTinkerApplicationLike", // delegate class
    "com.tencent.tinker.loader.TinkerLoader" // loader class
)

Tinker 的限制:

  • 必须重启生效(不能边玩边修)

  • 资源修改需要重新打包

  • dex 不能加新类,只能改已有类

Tinker 适合紧急 bugfix,不适合功能升级。

AndFix:方法级 hook

阿里 AndFix(Android Hot-Fix)走的是 Native 层路线:

方法调用栈:
  ├─ Java 方法 dispatch  →  ArtMethod 结构
  └─ Native 层 hook      →  替换 ArtMethod 的 entry_point

原理:ART 虚拟机中每个 Java 方法对应一个 ArtMethod 结构,里面有 entry_point_from_quick_compiled_code 字段。把这个字段改成新方法地址,旧方法被调用时实际执行新方法。不需要重启。

代码片段(AndFix 核心逻辑):

// andfix.cpp
void replaceMethod(JNIEnv *env, jclass clazz, jobject src, jobject dest) {
    if (art_method_replace == nullptr) return;
    
    // 拿到方法对象
    jobject srcMethod = env->ToReflectedMethod(clazz, src, JNI_TRUE);
    jobject destMethod = env->ToReflectedMethod(clazz, dest, JNI_TRUE);
    
    // 替换 entry_point
    art_method_replace(
        env, srcMethod, destMethod
    );
}

AndFix 的问题是:

  • 不支持新增字段、新增方法(只能替换方法体)

  • 复杂的泛型、lambda 处理时崩溃

  • 不同 ROM 的 ART 实现差异大,兼容性差

阿里后来推出 Sophix 用 Java 层的 dex 替换替代了 Native hook,兼顾 Tinker 的稳定性和 AndFix 的即时性。

Robust:编译期插桩派

美团 Robust 的思路独树一帜:在编译期给每个方法插一层"开关":

// 原始方法
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    initView();
}

// Robust 编译后
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    initView();
}

public boolean dispatch() {
    // 关键代码都在这里被引用
    // 被 patch 替换的就是这个方法
    return false;
}

执行时:

// 用户调用 onCreate → onCreate 引用 dispatch → dispatch 实际指向 patch

如果 patch 中替换了 dispatch 的实现,下次调用就走 patch 版本。

Robust 的好处是兼容性极好(不依赖 Native),坏处是必须用 Robust 编译才能生效,对原有构建流程侵入大。

各种方案的对比

方案

即时性

兼容性

接入成本

支持新增

Tinker

重启

高

中

限制

AndFix

即时

中

低

不支持

Sophix

即时+重启两种

高

低

支持

Robust

即时

高

高

限制

实战:自研热修复需要的关键能力

  1. Patch 生成:差分算法、签名校验、版本回滚机制

  2. Patch 下发:CDN 加速、强制更新、降级策略

  3. Patch 加载:dex 合成、类加载器隔离、ART/JVM 兼容

  4. Patch 监控:成功率、Cold start 影响、内存占用

  5. 回滚机制:patch 加载失败自动回滚

业务上的真实场景

热修复不是"万能钥匙"。大部分公司用热修复处理的是:

  • 启动崩溃、白屏黑屏

  • 严重 UI 问题

  • 安全漏洞(被劫持、隐私泄漏)

不要用热修复发新功能。运营层面的 A/B 测试、新功能灰度用 Play 的 Internal Testing 或专业 AB 测试平台。

一些反常识

  • Tinker 只支持 Android 4.0+,ART 后兼容改善

  • iOS 的 JSPatch/JSBox 不是热修复,是 JS 沙箱

  • Flutter 的 hot reload 是开发期工具,不是线上热修复

  • 小程序的热更新是另一套体系(云端代码包)

小结

热修复原理覆盖了 Android 的大部分底层:dex 加载、ART 运行时、Native hook、编译期插桩。学习热修复就是站在应用层往底层钻,能极大提升 Android 内功。但真到自己选型,无脑选 Sophix 或者干脆不用(直接发版)是大多数团队的合理选择,毕竟热修复的工程成本不低。

下一篇聊聊 Flutter 与 Android 互操作,这是当下跨平台的真正主流方案。


评论