Loading...

文章背景图

Tinker:Android 热修复框架接入与补丁打包指南

2026-10-23
0
- 字
- 分钟
|

Tinker 是腾讯开源的 Android 热修复框架,它允许 App 在不重新安装 APK 的情况下,动态更新 dex、native library(so)和资源文件。在 Android 开发中,线上 bug 修复的传统流程是改代码、发版、等用户升级,周期长且覆盖率低;热修复则让开发者可以直接向已安装的 App 下发一个小补丁包,让 App 加载修复后的代码,从而快速止损。Tinker 正是为这一场景设计的,它支持 dex 替换、so 替换、资源替换三种修复方式,并被微信等腾讯系 App 长期大规模使用。 在 Tinker 出现之前,Android 热修复领域已有 AndFix、Robust 等方案。AndFix 基于 native 方法替换,只能修复方法级别的 bug,且不支持资源与 so 修复;Robust 基于字节码插桩,需要在编译期改写所有方法,接入成本较高。Tinker 的思路是直接在 ClassLoader 层面追加一个补丁 dex,让补丁中的类优先于原 APK 中的类被加载,从而实现完整的 dex、so、资源替换。这种方案能力最全面,但也有自身的限制,例如不能修改 AndroidManifest.xml、补丁包体积相对较大、需要在启动时做合并。

Tinker 从微信内部孵化而来,经过多年线上验证,是国内 Android 热修复领域最有影响力的开源方案之一。本文基于仓库 README 逐节完整翻译整理,从项目背景、接入步骤、Ark 支持、已知限制到获取帮助做全面说明。

一、项目官方信息

项目 信息
项目名称 Tinker
GitHub 仓库 Tencent/tinker
出品方 腾讯
最新版本 1.9.15.1(见 Releases)
开源协议 BSD 3-Clause(见 LICENSE)
中文说明 Tinker Wiki
示例工程 tinker-sample-android

二、官方图片与架构

Tinker 的核心原理是在 App 运行时加载一个额外的 dex/so/资源补丁,让 ClassLoader 优先加载补丁中的类,从而"覆盖"原 APK 中的同名类。它不需要 Root,也不需要修改 AndroidManifest.xml,补丁以独立文件的形式存在 App 私有目录中。这种机制让 Tinker 可以在用户无感知的情况下修复线上问题,同时不需要用户重新下载 APK。对于用户量大、升级率低的 App,热修复能显著提升线上问题的修复效率。Tinker 在微信内部经过多年打磨,已经形成一套完整的补丁生成、下发、加载、监控体系,是国内最成熟的 Android 热修复方案之一,被众多企业级 App 采用,是国内 Android 热修复领域的事实标准之一,文档与生态成熟,易于上手。

从架构上看,Tinker 分为三层:编译期由 Gradle 插件负责对比基线包与新包、生成补丁文件;运行期由 tinker-android-lib 负责加载补丁 dex、合并资源、替换 so;业务层只需要处理补丁下载、进度展示与重启提示。这种分层让业务代码与热修复逻辑解耦,业务方只需要关注业务本身,补丁的生成与加载由框架统一处理,接入成本相对较低。

Tinker 提供了 Gradle 插件来自动生成补丁包,并在编译期处理 ProGuard 与 MultiDex 的 keep 规则,避免补丁加载时因类混淆或拆分而失败。补丁 dex 会被插入到 ClassLoader 的 dexElements 数组最前面,使得任何类加载请求都会先命中补丁中的类,从而实现"热替换"。资源修复通过替换 AssetManager 中的资源路径实现,so 修复则在加载 native 库时优先从补丁目录读取。这三种修复可以单独使用,也可以组合使用,由 tinkerFlags 控制。封面沿用本文既有官方仓库图,不再改动。

三、环境要求

  • Android Gradle Plugin:通过 tinker-patch-gradle-plugin 接入,需要在项目根 build.gradle 的 buildscript 中声明 classpath。
  • Android 插件版本:README 示例使用 1.9.1 版本号,实际请对齐最新 Release 1.9.15.1,保持插件与核心库版本一致,避免因版本错配导致补丁生成失败。Tinker 官方推荐固定一个版本号,全公司统一升级,不要在不同模块使用不同版本,避免冲突。
  • ProGuard:Tinker 已自动处理 ProGuard 配置,并自动生成 MultiDex keep 文件,无需手动写混淆规则。这一点对多 dex 应用尤其重要,因为类拆分后如果 keep 规则不对,补丁类可能无法被正确加载。Tinker 插件会在编译时自动生成 tinker_intermediates.txt 等中间文件,列出需要 keep 的类。
  • AndroidManifest.xml 限制:Tinker 不能动态修改 Manifest,因此不能新增四大组件。这是热修复框架的通用限制,因为 Manifest 在安装时已经解析并打包到系统中,运行时无法再注册新组件。需要新增组件的功能,只能通过发版解决,不能指望热修复。
  • 补丁生成:需要保留发布时的基线包与 mapping 文件,用于后续生成补丁。每次发布都应归档基线 APK,否则后续无法基于该版本生成补丁。这是 Tinker 接入后最容易被忽视的运维要求,建议在 CI 流程中自动归档每个 release 的基线包,便于后续回溯,避免基线包丢失。

四、部署步骤

接入 Tinker 分为声明插件、加依赖、改造 Application、用注解生成 Application 四步。下面逐步说明。

1. 声明 Gradle 插件

在项目根目录的 build.gradle 中加入:

buildscript {
    dependencies {
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')
    }
}

这一步把 Tinker 的补丁生成插件注册到构建脚本中,后续 app 模块才能 apply plugin: 'com.tencent.tinker.patch'。插件会自动注入补丁打包、mapping 处理等 Gradle task。

2. 在 app 模块应用插件并加依赖

在 app/build.gradle 的依赖块中加入 Tinker 的注解库与核心库,然后应用补丁插件。注解库用 provided 引入,因为它只在编译期起作用,不参与打包;核心库用 compile 引入,会被打进 APK,提供运行时补丁加载能力。

dependencies {
    //optional, help to generate the final application
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
    //tinker's main Android lib
    compile('com.tencent.tinker:tinker-android-lib:1.9.1')
}
...
...
apply plugin: 'com.tencent.tinker.patch'

tinker-android-anno 是可选的注解库,用于自动生成 Application 类;tinker-android-lib 是 Tinker 的核心库。应用 com.tencent.tinker.patch 插件后,Gradle 会在构建时注入补丁相关任务,包括生成补丁包、处理 mapping 文件等。

3. 改造 Application

这一步是接入 Tinker 最关键的改动。Android 系统在启动 App 时会先创建 Application 实例,Tinker 要求这个 Application 必须是 TinkerApplication 的子类,这样 Tinker 才能在 onCreate 之前完成补丁加载。如果你的 App 已经有一个继承 android.app.Application 的类,需要把它的业务逻辑移到一个继承 DefaultApplicationLike 的类中,例如:

-public class YourApplication extends Application {
+public class SampleApplicationLike extends DefaultApplicationLike {

然后把真正的 Application 类改为继承 TinkerApplication:

public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
      super(
        //tinkerFlags, which types is supported
        //dex only, library only, all support
        ShareConstants.TINKER_ENABLE_ALL,
        // This is passed as a string so the shell application does not
        // have a binary dependency on your ApplicationLifeCycle class.
        "tinker.sample.android.app.SampleApplicationLike");
    }
}

TINKER_ENABLE_ALL 表示同时支持 dex、so、资源三种修复。第二个参数是 ApplicationLike 的类名字符串,这样壳 Application 不需要在编译时依赖业务类,补丁替换业务类时也不会触发类加载冲突。这种设计把壳与业务解耦,是 Tinker 能在微信这种大型 App 中稳定运行的关键。业务类在 DefaultApplicationLike 中正常完成 onCreate、attachBaseContext 等生命周期,Tinker 会把这些生命周期转发给 ApplicationLike,业务代码无需感知壳与业务的分离。

4. 用注解自动生成 Application(推荐)

手写 TinkerApplication 子类虽然直接,但容易写错类名或 flags 参数。README 推荐使用 tinker-android-anno 注解来自动生成 Application,只需在 ApplicationLike 类上加注解:

@DefaultLifeCycle(
application = "tinker.sample.android.app.SampleApplication",             //application name to generate
flags = ShareConstants.TINKER_ENABLE_ALL)                                //tinkerFlags above
public class SampleApplicationLike extends DefaultApplicationLike

注解处理器会在编译期扫描 @DefaultLifeCycle 注解,生成一个继承 TinkerApplication 的类,类名就是注解中的 application 参数,flags 由注解中的 flags 参数决定,与手写构造函数一致。更详细的接入方式参考示例工程 SampleApplicationLike。更多 Tinker 配置项参考示例工程的 app/build.gradle。

五、Ark 支持

Tinker 支持在腾讯 Ark 编译器工具链下打补丁。Ark 是腾讯自研的 Android 编译工具链,对大型 App 的编译速度与包体积有优化,Tinker 专门为 Ark 场景提供了补丁生成与配置支持。对于已经使用 Ark 工具链的业务,Tinker 可以复用同一套补丁下发流程,不需要为 Ark 单独维护一套热修复方案,降低了多工具链并存的复杂度。

1. 构建补丁

bash build_patch_dexdiff.sh old=xxx new=xxx

其中 old 是有 bug 的旧 APK 绝对路径(非 Ark 编译),new 是修复后的新 APK 绝对路径(非 Ark 编译)。补丁文件会打包进 APK。脚本内部会对比两个 APK 的 dex、so、资源差异,只把变更部分打包进补丁,避免补丁过大。

2. 在 Ark 中编译

目前由 Ark 编译器团队负责编译,输出的补丁仍以未签名的 APK 格式打包。业务方不需要自己处理 Ark 编译,只需要把补丁交给 Ark 团队即可,后续加载流程与标准 Tinker 完全一致,业务无感知。

3. 配置补丁路径

对于 tinker-cli,在 tinker_config.xml 中加入 Ark 相关的 issue 配置,指定补丁存放路径与文件名。对于 Gradle,在 app/build.gradle 中加入同名配置块,路径与文件名与 XML 配置保持一致。

<issue id="arkHot">
   <path value="arkHot"/>         // path of patch
   <name value="patch.apk"/>      // name of patch
</issue>

对于 Gradle,在 app/build.gradle 中加入:

ark {
   path = "arkHot"         // path of patch
   name = "patch.apk"      // name of patch
}

补丁由 Ark 编译后放到上述路径,后续操作与 tinker-cli 或 gradle 一致。最终补丁 APK 包含两个文件:Android 用的 classes.dex,以及 Ark 用的带 so 的 patch.apk。这样无论是标准 Android 工具链还是 Ark 工具链,都能在同一个补丁包里加载修复。

六、使用方法

接入完成后,开发者需要用 Tinker 提供的工具对比基线包与修复包,生成补丁文件,再通过自己的下发通道把补丁推送到用户设备,App 在下次启动时加载补丁完成修复。具体的补丁打包与加载逻辑封装在 tinker-android-lib 中,业务层只需要处理补丁下载与展示。ProGuard 配置已由插件自动处理,Tinker 还会自动生成 MultiDex keep 文件,避免类被混淆或拆分后导致补丁加载失败。在实际使用中,建议保留每个发布版本的基线 APK 与 mapping 文件,这样当线上版本出现问题时,可以基于该版本快速生成补丁。补丁下发后,通常需要提示用户重启 App 以加载补丁。对于 so 与资源修复,Tinker 会在启动时完成合并,对用户透明。补丁文件本身很小,只包含变更的类与资源,用户下载流量低。业务方还需要在 App 中实现补丁下载、校验、存储与加载触发的逻辑,并在加载失败时自动回退到原 APK,避免补丁导致 App 无法启动。Tinker 官方建议补丁下发采用灰度策略,先在小范围用户群验证,再全量推送,以降低风险。同时建议保留回滚能力,当补丁引入新问题时可以快速下线。线上热修复是高风险操作,必须配合完善的监控与告警,发现补丁导致崩溃率上升时应立即停止下发并回滚,保护用户体验,避免补丁引发二次故障,影响线上稳定性。

七、已知问题(FAQ)

README 明确列出 Tinker 不能动态更新的场景:

  1. 不能更新 AndroidManifest.xml,例如新增 Android 四大组件(Activity、Service、Provider、Receiver)。
  2. 不支持部分系统版本为 android-21 的三星机型。这是三星在 Android 5.0 上的 ROM 兼容性问题,Tinker 无法在应用层修复,需要业务方自行规避或在升级时排除该机型,避免在少数三星设备上出现加载失败,影响用户体验。
  3. 受 Google Play 开发者分发协议限制,不能动态更新 APK。在海外市场上架 Google Play 时需要遵守该协议,不能通过 Tinker 下发 dex 修复,否则可能被 Google Play 下架,海外业务需要单独评估热修复方案,避免违反 Google Play 政策。

其他常见问题见 Tinker FAQ(中文)。遇到问题可以从以下渠道获取帮助:

  1. 学习示例工程 tinker-sample-android;
  2. 阅读源码;
  3. 阅读 Wiki 或 FAQ;
  4. 联系开发者获取支持。

补充说明:Tinker 补丁是基于基线包做差分的,因此生成补丁时必须指定与线上一致的基线包,否则补丁可能与用户本地的 APK 不一致,导致加载失败。补丁包本身是一个 apk 文件,里面包含变更后的 dex、so、res 以及一个 meta.txt 描述文件,Tinker 在加载时会校验签名与基线版本是否匹配。如果校验失败,补丁会被丢弃,App 继续使用原 APK,不会因为补丁损坏而崩溃。

贡献方面,详见 Tinker Contributing Guide。Tinker 采用 BSD 许可证,详见 LICENSE。

八、跳转链接

评论交流

文章目录