首页 / 新闻资讯 / 加固后热更新失效了怎么办?我们排查一周找到的解决方案
加固后热更新失效,是我们踩过最隐蔽的坑。

事情是这样的:我们的一款金融App接入了Bugly热修复,内测阶段一切正常。为了上架合规,我们给APK做了360加固。加固后重新测试热更新:补丁能下载、能合成,看起来没问题,就发了灰度。
结果灰度的崩溃率从0.2%飙到了9%。用户反馈App启动就闪退,而且只在华为、荣耀的部分机型上复现。我们本地死活复现不出来,连出问题的同款手机都试了——一切正常。
那一周我们排查了类加载时机、补丁合成日志、加固后的DEX结构,最后发现根本原因是:加固改变了ClassLoader的行为,导致补丁类没有被正确加载。
要理解这个问题,得先搞明白热更新的核心机制。
Tinker的方案是下发差量补丁包,在手机端与基线包的DEX合成完整的新DEX,然后通过自定义ClassLoader,将新DEX插入到dexElements数组的前面。这样系统加载类时,优先从补丁DEX里找,实现了类替换。
关键点在于:这个机制依赖ClassLoader对dexElements的操控。
加固工具(360加固、腾讯乐固、梆梆等)会对APK做DEX加密、代码抽取、SO混淆等操作。为了保护代码,加固后的APK在运行时往往会注入自己的ClassLoader,或者修改原有的类加载逻辑。
这就产生了冲突:
ClassNotFoundException或IncompatibleClassChangeError。我们同时用了Walle多渠道打包。加固后重新签名,渠道信息直接丢失,Bugly统计不到渠道数据。更糟的是,加固后Walle写入的渠道信息被清空,热修复补丁也因为签名变化而加载失败。

排查一周后,我们总结出一套能跑的方案。以下配置基于Bugly + Tinker + 360加固,其他加固方案同理。
Bugly从1.3.0版本开始支持加固模式(Tinker 1.7.9+)。在tinker-support.gradle中,必须把isProtectedApp设为true:
tinkerSupport { // 开启tinker-support插件 enable = true // 【关键】开启加固模式 isProtectedApp = true // 使用ProxyApplication模式(加固场景推荐) enableProxyApplication = true // 其他配置... baseApk = "${bakPath}/${baseApkDir}/app-release.apk" baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt" baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt" tinkerId = "1.0.1-patch"}isProtectedApp = true会让Tinker适配加固环境下的类加载器,避免补丁加载失败。
Bugly提供了两种接入方式,对应enableProxyApplication的值:
| 方式 | enableProxyApplication | 原理 | 加固场景稳定性 |
|---|---|---|---|
| 反射模式 | true | Bugly通过反射动态生成新的Application | 一般,可能有兼容性问题 |
| 继承模式 | false | 手动继承TinkerApplication,改造ApplicationLike | 高,推荐加固场景使用 |
我们一开始用了反射模式(enableProxyApplication = true),结果在部分机型上不稳定。换成继承模式后问题解决。

继承模式的具体做法:
tinker-support.gradle:enableProxyApplication = false // 关闭反射模式SampleApplication继承TinkerApplication:public class SampleApplication extends TinkerApplication { public SampleApplication() { super(ShareConstants.TINKER_ENABLE_ALL, "com.example.SampleApplicationLike", // ApplicationLike全限定名 "com.tencent.tinker.loader.TinkerLoader", false); }}SampleApplicationLike继承DefaultApplicationLike,把所有Application的代码移到这里:public class SampleApplicationLike extends DefaultApplicationLike { public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) { super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent); } @Override public void onCreate() { super.onCreate(); // 初始化Bugly Bugly.init(getApplication(), "YOUR_APP_ID", true); } @Override public void onBaseContextAttached(Context base) { super.onBaseContextAttached(base); // 必须安装MultiDex MultiDex.install(base); // 安装Tinker Beta.installTinker(); }}AndroidManifest.xml中声明SampleApplication:如果你用的是阿里云的Sophix,适配逻辑类似但细节不同。
Sophix本身支持加固包的热修复,但关键原则是:必须在加固前打补丁包。
最佳实践流程:
另外,如果用的是梆梆加固,需要在Sophix配置中忽略sophix文件夹,避免加固工具误处理。
加固 + Walle多渠道打包有两个坑:渠道信息丢失、签名被覆盖。
解决方案是用开源工具ProtectedApkResignerForWalle重新签名:
操作步骤:
./gradlew clean assembleRelease生成单个基线包ProtectedApkResignerForWalle工具,配置签名信息和加固后的APK路径python ApkResigner.py重新签名tinker-support.gradle中需要保持:
isProtectedApp = true // 必须开启这是最容易出错的地方。加固场景下,补丁必须基于加固前的包生成,不能基于加固后的包。
正确流程:
错误流程(会导致补丁无效):
配置完后,别急着上线。我们总结了一份验证清单:
/data/data/包名/files/tinker/目录是否有补丁文件)WalleChannelReader.getChannel()验证)Beta.cleanPatch())| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 补丁下载成功但修复不生效 | 类被加固框架提前加载 | 开启isProtectedApp = true,改用继承模式 |
| 部分机型崩溃,部分正常 | ClassLoader双亲委派机制差异 | 开启加固模式,增加enableProxyApplication = false |
| 加固后渠道信息丢失 | 加固重签名覆盖了Walle写入的信息 | 用ProtectedApkResignerForWalle重新签名 |
补丁合成失败,日志有TinkerException | 补丁基于加固后的包生成 | 必须用未加固的基线包生成补丁 |
| 华为/小米应用商店审核不通过 | 加固修改了签名结构 | 加固前保存原始签名,加固后用原签名重新签名 |
| 热修复方案 | 加固模式开关 | 最低版本要求 | 备注 |
|---|---|---|---|
| Bugly(Tinker) | isProtectedApp = true | Bugly 1.3.0+ / Tinker 1.7.9+ | 需配合enableProxyApplication = false稳定性更高 |
| Sophix | 服务端配置 + 加固前打补丁 | - | 梆梆加固需额外配置忽略sophix文件夹 |
| 腾讯云热修复 | 不支持官方适配 | - | 加固后热更新大概率失效,需自行处理 |
| 自有Tinker | 需手动适配类加载器 | - | 改造TinkerApplication,参考Bugly方案 |
数据来源:官方文档及实测
加固后热更新失效,本质是加固框架的ClassLoader与热修复的ClassLoader打架。解决方案也不复杂:开启加固模式、用继承方式接入、补丁必须在加固前生成、加固后重签名恢复渠道信息。
排查一周、崩溃率9%换来的教训是:加固和热更新必须在项目初期就一起接入测试,别等上架了才发现不兼容。希望这份方案能帮你少踩坑。