本文汇总了主流的热修复框架,并对每个框架的特性做了简单的介绍,主流方案比较如下图。
方案对比 | Andfix | 阿里百川HotFix | Sophix | 微信Tinker | 饿了么Amigo | 美团Robust |
---|---|---|---|---|---|---|
即时生效 | yes | yes | 同时支持即时生效和冷启动修复 | no | no | yes |
方法替换 | yes | yes | yes | yes | yes | yes |
so替换 | no | no | yes | yes | yes | no |
资源替换 | no | no | yes | yes | yes | no |
四大组件 | no | no | yes | no | yes | no |
实现原理 | 方法替换jni Hook | 方法替换jni Hook | 组合多种方法 | 修改dexElements数组 | 直接替换classLoader | java hook |
补丁生成 | 依赖apkpatch工具 | SophixPatchTool界面工具 | 便捷,图形化界面 | gradle命令生成 | 新Apk即是补丁文件 | gradle生成 |
补丁大小 | 较小 | 较小 | 不大,仅变动的资源和代码 | Davilk全量较大,Art差异包 | 较大 | 较小 |
接入成本 | 低 | 低 | 低 | 一般 | 低 | 高 |
性能开销 | 较小 | 较小 | 不大,仅变动的资源和代码 | 在App里合成,较大 | 全量替换,较大 | 较小 |
服务器支持 | 无 | 有,支持加密传输及签名校验 | 支持服务端控制 | TinkerPatch平台 | 有,已停止更新 | 无 |
Andfix
原理
方法替换是 AndFix 的热修复方案的关键,虚拟机在加载一个类的时候会将类中方法解析成 ArtMethod 结构体,结构体中保存着一些运行时的必要信息以及需要执行的指令指针地址。那么我们只要在 native 层将原方法的 ArtMethod 结构体替换成新方法的结构体,那么执行原方法的时候便会执行到新方法的指令,完成了方法替换。
AndFix提供了一种Native层hook Java层代码的思路,实现了动态的替换方法。在处理简单没有特别复杂的方法中有独特的优势,但因为在加载类时跳过了类装载过程直接设置为初始化完毕,所以不支持新增静态变量和方法。
集成方法
1.加载初始化PatchManager
2.添加patch文件:patchManager.addPatch(patchPath)
AndFix只需初始化库函数即可,对原有代码无侵入,使用简单。
补丁文件的生成(需要apkpatch工具)
1.准备新旧apk包 a.apk b.apk
2.下载apkpatch工具,运用工具生成patch
C:\apkpatch-1.0.3>apkpatch.bat -f a2.apk -t a1.apk -o out_dir -k keystore_path -p keystore_password -a alias -e entry_password
3.把patch文件推送给手机放到手机SD卡上执行升级
补丁文件里面包含APP签名,PATCH.MF文件里Patch_Classes字段包含了需要修复的类的名称信息。
优缺点
优点:集成简单,对App性能基本无影响,无需重启App可及时生效。
缺点:不能新增方法,兼容性不能保证。
传统的底层替换方式,不论是Dexposed、Andfix或者其他安全界的Hook方案,都是直接依赖修改虚拟机方法实体的具体字段。例如,改Dalvik方法的jni函数指针、改类或方法的访问权限等等。这样就带来一个很严重的问题,由于Android是开源的,各个手机厂商都可以对代码进行改造,而Andfix里ArtMethod的结构是根据公开的Android源码中的结构写死的。如果某个厂商对这个ArtMethod结构体进行了修改,就和原先开源代码里的结构不一致,那么在这个修改过了的设备上,通用性的替换机制就会出问题。这便是不稳定的根源。
AndFix原理分析
Android热修复框架AndFix原理解析及使用
阿里百川HotFix
阿里百川Hotfix解决方案是基于andfix方案的一个升级扩展,更加简化了接入流程,并加入了服务管理平台,可在阿里云服务器上下发更新补丁,并实时查看补丁更新情况。
Sophix
Sophix是阿里热修复的最新版本,提供了一套更加完美的客户端服务端一体的热更新方案,做到了图形界面一键打包、加密传输、签名校验和服务端控制发布与灰度功能,让你用最少的时间实现最强大可靠的全方位热更新。
官方接入参考
业界首个非侵入式热修复方案Sophix重磅推出,颠覆移动端传统更新流程
微信Tinker
原理
类替换
Tinker原理是通过classLoader机制修改pathclassloader中的dexElements数组顺序,把修复好的dex包加到原有dexElements数组前面来达到类替换的目的。
在App启动时通过dexclassloader读取补丁dex文件,用反射的方法获取到dexElements数组,同时拿到系统的pathclassLoader的dexElements,两者进行合并排序,把patch的相关dex信息放在数组前端,最后合并数组结果赋值给pathList保证classloader优先到patch中查找加载。
为什么要继承ApplicationLike类?
通常我们都是在Application中进行一些初始化的工作,包括tinker的初始化,那么application中所涉及到的类,在tinker初始化完成前就已经被类加载器所加载了,那么我们之前所说的通过将我们的补丁dex包插入到dexElements数组的前段的方法就不起作用了,Tinker推荐我们将自己的Application继承自ApplicationLike,在该类中先进行dex替换后,在加载application对象,同时tinker也就支持application的修复了。
集成方法
application需要继承applicationLike,代码有一定侵入性。
1.新dex与旧dex通过dex差分算法生成差异包 patch.dex
2.将patch dex下发到客户端,客户端将patch dex与旧dex合成为新的全量dex
3.将合成后的全量dex 插入到dex elements前面(此部分和QQ空间机制类似),完成修复
补丁生成
通过gradlew tinkerPatchRelease命令生成,需配置gradle参数,需要上一个版本的安装包用来比较差异。会自动将 apk,mapping 和 R 文件复制到tinker-old文件夹中。
补丁文件生成通过自研DexDiff算法实现,既解决了Dalvik平台的性能损耗问题,又解决了Art平台补丁包过大的问题。但这套方案的缺点在于占Rom体积比较大,微信考虑到移动设备的存储空间提升比较快,增加几十M的Rom空间这个代价可以接受。同时采用分平台合成的想法,即在Dalvik平台合成全量Dex,在Art平台合成需要的小Dex。
1.Dalvik全量合成,解决了插桩带来的性能损耗;
2.Art平台合成small dex,解决了全量合成方案占用Rom体积大的问题;
3.大部分情况下Art.info仅仅1-20K, 解决由于补丁包可能过大的问题;
优缺点
支持资源替换,so替换,兼容性好。
不支持修改AndroidManifest.xml,不支持四大组件的代理,没有Crash 启动保护 。
Dex合并内存消耗在vm head上,容易OOM,最后导致合并失败。
如果本身app占用内存已经比较高,可能容易导致app被系统杀掉。
github
Wiki
TinkerPatch 平台
微信Tinker的一切都在这里,包括源码
Tinker Application代理机制
简单易懂的tinker热修复原理分析
饿了么Amigo
原理
Amigo SDK是针对整个APP进行修复,不只是针对某个dex,又或者某个资源,又或者某个so文件。随意添加组件activity、receiver、service也是支持的。
Amigo 原理与 QQZone 的方案有些类似,QQZone,Tinker,Nuwa这类方案是通过修改PathClassLoader中的dex实现的,Amigo则是釜底抽薪直接替换ClassLoader。同时进一步实现了 so 文件、资源文件、四大组件的修复.
集成方法
集成简单,代码无侵入性。
Amigo.workLater(context, patchApkFile, callback);
Amigo.work(context, patchApkFile);
补丁生成
无需单独生成,新Apk即是补丁包,全量替换。
优缺点
兼容性好,支持更多的修复方式。集成简单。
补丁较大,占用资源较多。
美团Robust
原理
参考了 Instant Run 热插拔原理。
Robust插件对每个产品代码的每个函数都在编译打包阶段自动的插入了一段代码,插入过程对业务开发是完全透明。
编译打包阶段自动为每个class都增加了一个类型为ChangeQuickRedirect的静态成员,而在每个方法前都插入了使用changeQuickRedirect相关的逻辑,当 changeQuickRedirect不为null时,可能会执行到accessDispatch从而替换掉之前老的逻辑,达到fix的目的。由于使用的是最基础的DexClassLoade并没有更改原有的代码逻辑,也就不存在兼容性问题。
集成方法
1.集成了 Robust 后,生成 apk。保存期间的混淆文件 mapping.txt,以及 Robust 生成记录文件 methodMap.robust
2.使用注解 @Modify 或者方法 RobustModify.modify() 标注需要修复的方法
3.开启补丁插件,执行生成 apk 命令,获得补丁包 patch.jar
4.通过推送或者接口的形式,通知 app 有补丁,需要修复
5.加载补丁文件不需要重新启动应用
代码修改需要添加注释,修改,标记。集成对原有代码有侵入,接入成本较高。
补丁生成
Robust补丁自动化,为Robust自动生成补丁,使用者只需要提交修改完bug后的代码,运行和线上apk打包同样的gradle命令即可,会在项目的app/build/outputs/robust目录下生成补丁。
优缺点
优点:
几乎不会影响性能(方法调用,冷启动)
支持Android2.3-8.x版本
高兼容性(Robust只是在正常的使用DexClassLoader)、高稳定性,修复成功率高达99.9%
补丁实时生效,不需要重新启动
支持方法级别的修复,包括静态方法
支持增加方法和类
支持ProGuard的混淆、内联、优化等操作
缺点:
代码是侵入式的,会在原有的类中加入相关代码
so和资源的替换暂时不支持
会增大apk的体积,平均一个函数会比原来增加17.47个字节,10万个函数会增加1.67M。
会增加少量方法数,使用了Robust插件后,原来能被ProGuard内联的函数不能被内联了
GitHub
GitHub Wiki
Android 美团Robust热更新 使用入门
补充
SO库热修复方案
so库的修复本质是对native方法的修复和替换,和类加载方案类似,可以把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,使得优先加载补丁库而不是原来的库来达到修复目的。
资源替换参考Instant run
1.通过反射替换掉原有的AssetManager
2.找到引用了原AssetManager对象的字段并替换为新的引用。