返回产品动态
Shiply

腾讯 Shiply:让线上热修复更快,发布更稳

Shiply

摘要

线上 Crash、核心功能异常、错误逻辑……当问题已经影响用户,等待下一个应用版本完成审核和覆盖,往往意味着更长的故障窗口。


应用热修复因此而生。但让一段修复代码运行起来只是开始,真正困难的是:如何跨越复杂的系统版本和设备环境,把补丁安全、稳定、可控地送达用户。


本文将从 Android 热修复的技术演进讲起,依次讲清虚拟机 Hook、函数插桩和全量 Dex 替换的原理,以及方案为什么一代代向前走。它们也是腾讯 Shiply(以下简称 Shiply)先后探索的底层技术方向。在此基础上,我们还将介绍 Shiply 如何通过发布管理、流程控制、灰度放量和质量监控,构建一套完整的线上修复体系。


一、代码已经修好了,用户的问题为什么还在?

对于移动应用来说,线上问题的修复通常包含两段完全不同的时间。


第一段是研发时间:定位问题、修改代码、完成测试。

第二段是版本时间:重新打包、提交应用市场、等待审核、发布新版本,再等待用户逐步升级。


很多时候,代码修复只需要数小时,但传统版本发布通常需要 1 天到数天;热修复可以将补丁发布时间缩短到数小时至 1 天。对于高频 Crash、支付链路异常、核心页面不可用等问题,这段时间差会持续放大影响。


热修复让“代码已经修好”与“用户侧问题消失”之间的距离更短。

但“更快”并不是唯一目标。


Android 设备有不同的系统版本、芯片架构和厂商实现,应用自身还要面对混淆、代码优化、多 Dex、资源和动态库等复杂问题。补丁一旦出错,可能让原本局部的问题扩大为新的线上事故。


热修复真正困难的,从来不只是让一段新代码运行起来,而是让它在复杂的 Android 生态中安全、稳定、可控地抵达用户。



二、Android 热修复究竟在“修”什么?

Android 应用中的 Java、Kotlin 代码经过编译后,会以 Dex 的形式进入 APK。应用启动时,ClassLoader 负责找到并加载类,Dalvik 或 ART 虚拟机负责执行类中的方法。


如果把一次方法调用简化来看,它大致经历三个环节:

1. 找到目标类;

2. 找到目标方法;

3. 跳转到对应的代码入口执行。

热修复的核心,就是在这条链路上想办法让系统执行新代码。


围绕这个目标,Shiply 的底层方案走过三代:

  • 虚拟机 Hook:直接修改方法入口和虚拟机中的类、方法状态,先把“换上新代码”做通;

  • 函数插桩:针对虚拟机内部实现持续变化的问题,在编译期为方法预留跳转,改用应用层机制完成替换;

  • 全量 Dex 替换:再把修复对象从单个方法抬到完整代码,并覆盖资源和动态库。Shiply 当前方案以此为基础,并结合大规模业务实践持续优化。


每一代都在解决上一代暴露出来的主要问题:先让修复发生,再摆脱对虚拟机私有实现的依赖,最后让复杂修改也能稳定落地。


三、虚拟机 Hook 路线:探索与边界

热修复的早期探索中,一条很自然的思路是:既然每个方法最终都有执行入口,能不能直接把旧方法的入口换成新方法?


虚拟机 Hook 沿着这条思路,从 ART 的运行机制入手,成为 Shiply 的第一代底层方案。


1. 虚拟机 Hook 如何完成代码替换?

在 ART 中,每个方法都对应一个 ArtMethod 对象,其中保存了方法信息和执行入口。与此同时,为了提升启动和运行效率,系统还会进行 AOT/JIT 编译、类预加载和函数内联。


这意味着,补丁不能只是加载一个新类。


如果旧类已经进入 ClassTable 或 DexCache,ClassLoader 可能仍会找到旧类;如果旧方法已经被编译成本地指令,甚至被内联到其他方法中,仅替换原方法也可能无法真正改变执行结果。


为此,虚拟机 Hook 需要完成一组相互配合的操作:

  • 将修复类放入补丁 Dex,并让 ClassLoader 优先找到它;

  • 清理 ClassTable 和 DexCache 中相关的旧类缓存;

  • 分析可能受函数内联影响的调用方;

  • 将相关方法切换到解释执行,绕开 AOT 已经生成的本地代码。

这套方案的优势很明显:不需要给所有业务代码预先插桩,对应用日常运行性能影响较小。当修改范围可控时,它能以较轻的代价完成线上问题修复。


2. 为什么这条路线越来越难?

它的问题也来自同一个地方:与虚拟机内部实现结合得太深。


ART 的内部数据结构、符号和编译策略会随着系统演进而改变,不同手机厂商也可能进行定制。为了清理缓存、修改方法入口和处理内联,框架必须持续适配这些变化。


当类的继承关系、字段或方法结构发生变化时,受影响的范围还可能沿着子类和调用关系扩散。补丁越复杂,需要重新处理的类和方法越多,补丁加载耗时和兼容风险也会随之上升。


随着 APEX 动态升级、跨 Dex 内联等机制出现,仅根据 Android 系统版本判断运行环境也不再充分。虚拟机可能被独立升级,函数内联的分析范围也进一步扩大。


这一代已经能以较轻的代价完成函数体修改和新增类。当修复继续走向类结构变化和更大规模的代码变更时,追逐虚拟机实现的成本会成为主要障碍。


虚拟机 Hook 证明热修复可以把单次修复做轻。它留下的问题是:框架必须持续追逐虚拟机实现,系统一变,修复能力就要重新适配。


四、函数插桩路线:跨过虚拟机适配

虚拟机 Hook 的主要压力来自虚拟机内部实现。函数插桩改为不碰这些私有结构:在编译阶段就为每个可能被修复的方法预留跳转入口。


函数插桩选择在最终 Dex 字节码阶段分析和提取变更。


1. 在每个方法前放一个“岔路口”


插桩后的方法,可以简化理解为:

if (patchIsAvailable()) {         return dispatchToPatch(); } return originalMethod();


没有补丁时,方法继续执行原逻辑;补丁下发后,应用动态加载补丁 Dex,并将特定方法转发到新的实现。


整个过程使用的是应用层可用机制,不需要修改 ArtMethod,也不依赖 ART 的私有数据结构,因此具备更好的系统兼容性。


如果补丁不依赖已经错过的初始化时机,也不改变运行中的数据状态,它还可以立即生效,无需等待应用重启。

2. 真正困难的是自动生成补丁


插入一段判断逻辑并不难,难的是准确识别两次构建之间发生了什么变化,并自动生成能够调用原有代码的补丁。


从源码到最终 Dex,中间还要经历脱糖、混淆和 R8 优化。函数名、参数和访问权限可能发生变化,部分函数可能被内联甚至删除。即使业务只改动了少量代码,两次构建产物也可能出现额外差异。


函数插桩将差异分析放到最终 Dex 字节码阶段,以减少中间构建过程的干扰,同时通过定制 R8、复用 Mapping 和函数 ID 等手段保持两次构建的一致性。


虚拟机兼容问题因此缓解,方法级修复的边界也更清楚地露了出来。


插桩方案仍然以“替换函数实现”为中心。修改类继承、访问权限、构造函数和注解等结构性变化,往往存在限制。新增字段还要额外解决变量存储、对象绑定和垃圾回收问题。


此外,每个被插桩的方法都会增加指令。即使通过过滤第三方库、缩小插桩范围和性能模式进行优化,包体与正常执行路径仍需承担一定成本。


对于开发者而言,补丁代码还需要遵守更多规则。复杂语法、R8 优化结果或无关代码差异,都可能增加构建失败和排障成本。代码规模越大,这些成本越重。


函数插桩解决了虚拟机兼容问题,满足条件时补丁还可以立即生效。下一代要突破的,是方法级修复盖不住的类结构、资源和长期工程成本。


五、全量 Dex 路线:Shiply 当前的技术选择

前两代都在已经加载的方法上做文章:虚拟机 Hook 改入口,函数插桩在入口前加跳转。全量 Dex 走到下一步:在应用下一次启动时,让 ClassLoader 从一开始就加载修复后的代码。


Shiply 当前的底层方案以全量 Dex 替换为基础,并结合大规模业务实践,在补丁安装成功率、生效速度、系统兼容性和接入便利性等方面做了持续优化。


这里的“全量”首先指代码层最终以完整 Dex 的形态加载。与此同时,方案还支持资源和动态库的替换更新。三类能力结合后,更新对象从某个方法扩大到应用的代码、资源和动态库,更接近一次完整的应用内容更新。


1. 补丁并不是简单地“塞进一个新 Dex”


全量 Dex 的补丁制作从两个 APK 开始:

  • 线上正在运行的基准 APK;

  • 包含修复代码的新 APK。

构建工具比较两者的 Dex、资源和动态库差异,生成体积更小的差分补丁。


补丁到达设备后,客户端基于基准 APK 中的旧 Dex 和差分数据,重新合成修复后的完整 Dex,并进行完整性校验和必要的优化。应用后续启动时,再由新的 ClassLoader 加载这套代码。

因此,“全量 Dex 替换”描述的是最终加载形态,并不意味着必须把完整 Dex 直接下发给用户。网络传输的仍可以是差分数据。


2. 能力边界:问题修复之外,仍受发布规范约束

虚拟机 Hook 围绕方法入口,函数插桩围绕预埋的方法跳转,全量 Dex 替换则直接更新完整类定义。新增或修改类、字段和方法都可以进入这套完整 Dex,业务侧按正常的代码变更来编写补丁。


在代码之外,全量 Dex 还支持资源和动态库更新。

除 AndroidManifest.xml、RemoteView 等少量受系统管理的内容外,可以覆盖多数应用内容。


这套能力的首要用途仍是修复 Crash 和错误逻辑。在变更范围可控、经过充分验证并符合发布规范的前提下,也可以承载轻量的功能级更新,并结合灰度发布,按线上数据逐步扩大覆盖。


全量 Dex 把能力边界放宽到完整的类、资源和动态库;一旦超出问题修复,就需要更严格的验证和发布控制。


3. 合成与重启成本,可以用工程手段压低

设备需要完成差分合成、校验和 Dex 优化,补丁安装时间通常高于轻量方法补丁。由于旧类一旦加载后无法直接卸载,代码补丁通常需要在后续启动时生效。


新合成的 Dex 也难以完全复用原安装过程中的所有预加载和优化成果,冷启动与运行性能需要单独优化。


这些成本集中在安装和启动阶段,可以通过异步安装、DexOpt、分阶段下发和性能优化来管理。Shiply 在全量 Dex 之上持续优化的,也正是补丁安装成功率、生效速度、系统兼容性和接入便利性。


在当前 Android 生态和大规模业务里,全量 Dex 是修复更完整、兼容更稳、开发约束更少、也更能长期维护的方案。Shiply 因此把它作为当前的底层技术选择。


六、为什么方案从 Hook 走到插桩,再走到全量 Dex

虚拟机 Hook 先把方法入口换通,用较轻的运行时成本完成函数级修复。系统版本、厂商实现和内联策略一变,这套深入虚拟机的做法就要跟着适配。


函数插桩接着把跳转预埋到编译期。修复不再依赖 ART 私有结构,兼容性明显提高,满足条件时还可以立即生效。修复对象仍然主要是方法实现。类结构、字段、资源和构建一致性,会把成本留在工具链和开发规则上。


全量 Dex 再往前走了一步。设备上最终加载的是修复后的完整代码,类、字段、方法、资源和动态库都能进入同一次更新,业务侧按正常代码变更编写补丁。合成和下次启动生效这些成本,集中在安装阶段,并且已经有明确的工程手段持续优化。


当应用具备庞大的代码规模、复杂的工程体系和广泛的设备覆盖时,方案要同时回答:

  • 能否覆盖真实业务中的复杂修改?

  • 能否适应持续变化的 Android 系统和构建工具链?

  • 普通业务开发者能否快速、正确地制作补丁?

  • 补丁能否充分验证,并在出现异常时控制影响范围?

  • 整套能力能否长期维护,让普通研发团队持续使用?


全量 Dex 在这几项上同时成立,所以热修复的工程实践走到这条路线:

1. 加载机制从虚拟机私有实现,走到更稳定的 ClassLoader 和完整代码替换;

2. 修复目标从尽快换掉一个方法,走到兼容性、修复完整性和结果可验证优先;

3. 产品形态从单一客户端 SDK,走到覆盖构建、发布、灰度和监控的完整工程体系。


这也是 Shiply 采用全量 Dex,并在底层修复能力之上继续建设平台化能力的原因。


对大规模 Android 应用来说,全量 Dex 是当前更完整、也更可持续的热修复方案。


七、腾讯 Shiply:从底层能力到完整热修复解决方案

一套引擎可以解决“新代码如何被加载”,却不能独立回答下面这些问题:

  • 补丁是否基于正确的线上版本构建?

  • 补丁包含的变化是否符合预期?

  • 谁可以发布,发布前是否经过审批?

  • 应该先覆盖哪些用户,如何逐步放量?

  • 补丁是否成功下载、安装并生效?

  • 发布后 Crash 和核心指标是否发生异常?


这些问题共同决定了一次热修复能否真正安全落地。


因此,Shiply 热修复可以理解为四个相互配合的层次:

  • 底层修复与接入:负责 Dex、资源和动态库等内容的修复与加载,并降低复杂工程的接入成本;

  • 构建与校验层:负责基准管理、差异生成、产物检查和兼容验证;

  • 发布与灰度层:负责任务管理、审批控制和分阶段放量;

  • 数据监控与安全治理层:负责状态采样、稳定性观察和风险发现。


前文三条技术路线,解决的是修复代码如何运行的问题。Shiply 进一步把底层修复与接入、构建、发布和监控接成覆盖补丁完整生命周期的解决方案。


1. 底层修复与接入,兼容复杂工程

Shiply 当前的底层方案以全量 Dex 替换为基础,并结合内部业务实践,持续优化补丁安装成功率、生效速度、系统兼容性和接入便利性。


在默认接入方式之外,Shiply 还提供 Application 代理接入方式,降低业务工程的改造成本,并改善与 Hilt 等依赖注入框架的兼容问题。


应用加固通常会改变 APK 的包结构或代码加载流程,常规补丁制作与加载链路可能因此受到影响。针对这类场景,Shiply 兼容主流应用加固方案,并提供专用的补丁制作模式,使补丁在应用加固环境下仍能正常完成构建、安装和运行时加载。


面对 Android 系统和构建工具链的持续变化,Shiply 也持续推进系统版本、AGP 和复杂构建场景的适配,让业务不必独立承担底层框架维护成本。


2. 让补丁构建结果可检查

热修复依赖基准 APK、新 APK、Mapping、资源映射等构建产物。任何基准选择错误或构建环境不一致,都可能让补丁包含非预期变化。


因此,安全的热修复流程不能止于“构建成功”,还需要检查:

  • 基准版本是否正确;

  • 补丁产物是否完整;

  • 实际变更是否符合修复意图;

  • Dex、资源和动态库差异是否在预期范围内;

  • 补丁能否在目标环境中正确安装和加载。


将这些经验固化为工具和流程,可以减少对个人经验的依赖。


3. 用审批与灰度控制发布半径

补丁越快,越需要克制。


Shiply 提供补丁任务管理、发布审批和灰度放量能力。团队可以先在内部或小范围用户中验证,再根据效果逐步扩大覆盖,而不是一次性将风险暴露给所有用户。


这套机制的价值并不只是“多一个审批按钮”,而是让每次补丁发布都具备清晰的责任边界、操作记录和放量节奏。


4. 用数据确认补丁真的解决了问题

补丁上传成功,不代表问题已经解决;补丁下载成功,也不代表修复逻辑已经生效。


Shiply 围绕补丁生命周期采集关键数据,帮助团队观察下载、安装和生效情况,并按应用版本、系统或设备等维度分析覆盖表现。


发布数据还可以与稳定性监控和告警联动。团队不仅能看到补丁覆盖了多少用户,还能继续判断:

  • 原问题指标是否下降;

  • 是否出现新的 Crash 或异常;

  • 特定系统和机型是否存在异常集中;

  • 当前数据是否支持继续扩大灰度。

热修复引擎解决“能不能修”,工程体系解决“敢不敢发、能不能持续使用”。


八、规模化实践:从腾讯内部到多行业应用

一套热修复能力是否成熟,不只取决于它能否完成一次补丁发布,还要看它能否适应不同规模、不同工程架构和不同稳定性要求的应用。


腾讯内部:经过大量 App 实践验证

目前,腾讯内部已有超过 100 个业务接入 Shiply 热修复,覆盖亿级终端用户,日均请求量达到百亿级。这些业务在应用体量、技术架构、版本节奏和用户规模上差异明显,也为 Shiply 提供了复杂的验证环境。


这些持续积累的业务实践,不仅推动了底层方案在补丁安装成功率、生效速度和系统兼容性等方面的优化,也逐步沉淀出构建校验、发布审批、灰度控制和质量监控等平台能力。

外部服务:覆盖多个典型行业

在外部,Shiply 热修复也已服务于多个行业,典型行业包括:

  • 银行、基金与证券:核心链路异常和紧急逻辑错误的快速修复,变更要可审批、可灰度、可追溯;

  • 互联网应用:高频 Crash、功能异常和轻量功能灰度,缩短问题恢复与新功能覆盖周期;

  • 汽车与出行:复杂业务链路异常、系统与机型兼容,以及影响核心体验的线上故障。


从腾讯内部复杂业务的长期实践,到外部多行业场景的持续验证,Shiply 热修复已经不再只是一项底层技术能力,而是一套能够适应不同业务要求的完整解决方案。



九、真正成熟的热修复,是对风险的系统管理

Shiply 的底层方案从虚拟机 Hook、函数插桩走到全量 Dex。当前以全量 Dex 替换为基础,因为它在复杂业务、大规模设备和长期演进中更完整,也更稳。


在此之上,接入适配、补丁构建、审批、灰度、数据采样和稳定性监控组成闭环,热修复才能从少数专家掌握的应急技术,变成研发团队可以持续信任的稳定性基础设施。


修得快,是热修复的价值;修得稳、发得准、看得见,才是热修复走向成熟的标志。


如果你的应用也面临线上问题恢复慢、版本覆盖周期长、补丁发布缺少安全保障等挑战,欢迎了解 腾讯 Shiply 应用热修复,让每一次线上修复更快、更稳、更可控。


Shiply(全场景发布—Shiply)是面向端的一站式发布平台,作为腾讯端服务联盟(TDS 腾讯端服务)的重要成员,提供配置与开关发布、资源发布、RN热更新、Flutter动态化、Android插件化框架、热修复、应用内升级、市场发布、应用内测等服务,帮助业务快速、安全地进行客户端功能迭代和上线。