Tinker
原理
类修复
通过自研的DexDiff算法生成差分包,在客户端和旧的dex合成新的dex文件,利用Android在加载一个类的时候是按顺序从dexElements数组查找加载的,已经加载过了就不会继续查找的原理,在下次app启动的时候将修复好的新dex插入到dexElements的前面。通过合成新的dex包解决CLASS_ISPREVERIFIED的问题。
CLASS_ISPREVERIFIED问题
就是当类A只引用了同一个dex中其它类时,A会被打上CLASS_ISPREVERIFIED,A如果再去引用别的dex中的类,就会报CLASS_ISPREVERIFIED错误。
资源文件修复
- 在生成apk的时候通过public.xml固定资源id,防止补丁包中的资源id和原有apk中的资源id冲突
- 遍历LoadedApk,将resDir替换为新合成的资源目录路径
- 创建新的AssetManager,调用addAssetPath添加补丁路径,反射获取ResourceManager的resources缓存,设置ResourceImpl的mAsset变量为新创建的AssetManager,然后清除缓存,更新资源
so修复
- 同样通过差分包合成新的so文件
- 需要手动根据设备abi去加载so,使用System.loadLibrary来加载
优点
- 支持类修复、资源修复、so修复,涵盖了绝大部分需要修复的地方
不足
- 不能更新AndroidManifest.xml
- 不支持即时生效,需要重启app
- 不支持部分三星android-21机型
- 需要改动原来项目的Application,因为Application类的加载是早于热修复框架加载。
- 合成新的dex比较占内存,需要开一个新的进程合并
Robust
原理
通过在产品的每个方法中插入一段代码,判断changeQuickRedirect不为null就执行changeQuickRedirect的方法 原方法:
public long getIndex() {
return 100;
}
插桩后的代码
public static ChangeQuickRedirect changeQuickRedirect;
public long getIndex() {
if(changeQuickRedirect != null) {
//PatchProxy中封装了获取当前className和methodName的逻辑,
//并在其内部最终调用了changeQuickRedirect的对应函数
if(PatchProxy.isSupport(new Object[0], this, changeQuickRedirect, false)) {
return ((Long)PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue();
}
}
return 100L;
}
优点:
- 几乎不会影响性能(方法调用,冷启动)
- 支持Android2.3-8.x版本
- 高兼容性(Robust只是在正常的使用DexClassLoader)、高稳定性,修复成功率高达99.9%
- 补丁实时生效,不需要重新启动
- 支持方法级别的修复,包括静态方法
- 支持增加方法和类
- 支持ProGuard的混淆、内联、优化等操作 缺点:
- 代码是侵入式的,会在原有的类中加入相关代码
- 不支持so和资源的替换
- 会增大apk的体积,平均一个函数会比原来增加17.47个字节,10万个函数会增加1.67M。
- 会增加少量方法数,使用了Robust插件后,原来能被ProGuard内联的函数不能被内联了
AndFix
原理
Art虚拟机:每个类方法在Native中都对应一个ArtMethod结构体 Dalvik虚拟机:通过FromReflectedMethod获取Method结构体的指针 将获取到的结构体的属性替换为修复的方法的属性,达到修复效果
优点
- 即时生效
缺点
- 兼容性比较差,Dalvik虚拟机和ART虚拟机需要两种实现,各个厂商和各个版本的ArtMethod会有不一致的地方,需要分别兼容,目前只兼容到Android 7.0,已经没有维护了,升级到Sophix,不过Sophix没开源
QZone
原理
跟Tinker一样是在dexElements插入新dex,但是没有将补丁dex和旧的dex合并生成新的dex包, 为了解决CLASS_ISPREVERIFIED问题,会在应用中包含hack.dex,这个dex只有AntilazyLoad一个类,然后在每个类中插桩,使每个类都引用这个类,使类不会被打上CLASS_ISPREVERIFIED。
优点
- 不用做dex合并,相对Tinker内存占用少
缺点
-
Dalvik虚拟机,APP在安装的时候会被执行dexopt操作,同一个dex文件内的Class会被打上CLASS_ISPREVERIFIED标志,而补丁包中的类并没有打上此标志,因此抛出异常。解决方法就是在第一次打包APK时让所有类都引用另一个dex文件中的类,这样所有的类始终不会打上CLASS_ISPREVERIFIED标志,因此补丁包可以顺利加载,但是Dalvik虚拟机在检测到一个类未打上CLASS_ISPREVERIFIED之后会再次在类加载的时候进行dexopt相关的操作,如果一次性加载很多类,速度将明显变慢。
-
Art虚拟机,dex文件最终会编译成本地机器码,在dex2oat时fast *已经将各个类的地址写死,若补丁包中的类出现字段或者方法的修改,会出现内存地址错乱,解决办法是将这个类的父类和调用这个类的类都加入补丁包。但这样会导致补丁包急剧增大。