热修复是目前Android市场上比较火的一种“黑科技”,主要用于解决线上重大问题的紧急修复,主流的热修复技术就是“即时无感打补丁”,提升了问题处理能力,优化了用户的使用体验。
热修复技术原理
目前市场上主流的热修复技术,一般分为类加载机制、底层替换机制、Instant Run热插拔机制。
底层替换机制
底层替换方案是直接在Native层修改原有类。传统的底层替换方案是修改虚拟机方法、JNI函数指针和访问修饰符等,这会严重受制于Android手机厂商的定制化系统。Sophix的底层替换方案是在此思想的基础上的优化,针对ArtMethod结构体整体替换,优于之前AndFix针对ArtMethod结构体内方法单独替换的思路。
由于底层替换不会再次加载新类,是直接替换了方法或者类,可立即生效不需重启App。
类加载机制
在APP重新启动后,ClassLoader会遍历Element数组dexElements(每个dex文件对应一个Element),找到对应类就返回,不会再去加载有Bug的那个类了。基于此,我们把这个修复后的文件放在Dex文件中,并让其排在dexElements数组靠前位置即可。
类加载机制需要重启方有效,因为类无法被卸载,只有重新调用ClassLoader加载类,即需要重启App,不能即时生效。
Instant Run热插拔机制
在此机制下,编译打包阶段自动为每个class都增加一个类型为ChangeQuickRedirect的静态成员,而在每个方法前都插入了使用changeQuickRedirect相关的逻辑,当changeQuickRedirect不为null时,会执行access$dispatch方法,最终执行到替换类对应方法的新逻辑,达到fix的目的。
热插拔机制的热修复优势在于即时生效,但其使用了插桩思想,性能开销很大。
补充说明下Instant Run的热插拔、温插拔、冷插拔的概念。
- HotSwap(热插拔):修改方法实现后代码可以实时生效,不需要重启App也不需要重启activity,只要加载补丁之后就可以马上生效。通常情况下,热插拔只适用于方法体内部的逻辑改变。
- WarmSwap(温插拔):主要针对于需要修改或删除资源的情况。温插拔不需要重启App,但是需要重启当前的activity后才能生效。仅适用于开发阶段。
- ColdSwap(冷插拔):主要针对于改变了类的结构、继承关系、实现接口等情况,此时因为类结构本身被改变了,需要重新去加载这个类,所以需要重启App之后才能生效。
热修复方案对比
基于类加载机制的热修复技术有:Tinker、QQ空间超级补丁、手机QQ的QFix、Amigo和Nuwa等。
- QQ空间超级补丁/Nuwa:将补丁包放在Element数组的第一个元素得以优先加载。
- Tinker:将新旧apk做了diff,并将得到的patch.dex与手机中的apk的classes.dex合并,生成新的class.dex,并在运行时通过反射将classes.dex放在Element数组的第一个元素。
- Amigo:将补丁包中的每个dex对应的Element取出来,之后形成新的Element数组,在运行时通过反射用新的Element数组替换掉现有的Element数组。
基于底层替换机制的热修复技术有:AnFix,Dexposed,阿里百川,Sophix。
基于Instant Run机制的热修复技术有:Robust和Aceso。
目前市场上较成熟的热修复方案,优劣对比如下表。
方案对比 | Sophix | Tinker | Amigo |
---|---|---|---|
DEX修复 | 同时支持即时生效和冷启动修复 | 冷启动修复 | 冷启动修复 |
资源修复 | 差量包,不用合成 | 差量包,需要合成 | 全量包,不用合成 |
SO库更新 | 插桩实现,开发透明 | 替换接口,开发不透明 | 插桩实现,开发透明 |
性能损耗 | 低,仅冷启动情况下有些损耗 | 高,有合成操作 | 低,全量替换 |
四大组件 | 不能修复 | 不能修复 | 能修复 |
生成补丁 | 直接选择已经编好的新旧包在本地生成 | 编译新包时设置基线包 | 上传完整新包到服务端 |
补丁大小 | 小 | 小 | 大 |
接入成本 | 傻瓜式接入 | 复杂 | 一般 |
Android版本 | 全部支持 | 全部支持 | 全部支持 |
安全机制 | 加密传输及签名校验 | 加密传输及签名校验 | 加密传输及签名校验 |
服务端支持 | 支持服务端控制 | 支持服务端控制 | 支持服务端控制 |
四大组件的修复,须要在 AndroidManifest.xml 里预先插入代理组件,并尽可能声明所有权限,会造成原有APP更多臃肿代码,侵入性强。
调研结果
建议采用阿里体系的Sophix热修复方案,主要原因如下:
- 全量替换ArtMethod结构体,忽略内部结构差异,适配全版本、多平台系统定制(已验证兼容Android O版本);
- 结合类加载机制思想,优化dex粒度,采用dex文件级别的类插桩方案,降低性能损耗;
- 联合底层替换机制和类加载机制,优势互补,支持根据代码变动情况或机型支持情况灵活选择最适合方案。
- 采用别于Instant Run温插拔的机制,直接在原有的AssetManager对象上进行重构变动资源的补丁包,不影响外在引用逻辑。
简单说就是:人无我有,人有我优。所以建议Sophix。