学习资料:《Android进阶解密》
常见的热修复框架有阿里的AndFix、Dexposed、阿里百川和Sophix,腾讯的微信Tinker、QQ空间的超级补丁和手Q的QFix,其他知名大厂的有美团的Robust、饿了么的Amigo、美丽说蘑菇街的Aceso,以及其他的RocooFix、Nuwa和AnoleFix等等。
热修复框架的主要分为代码修复、资源修复和动态链接库修复,每个库使用修复的原理都有所区别
-
资源修复
大多热修复框架的资源修复都参考了Instant Run的资源修复原理
-
Instant Run部署三种方式
-
Hot Swap
效率最高的部署,代码的增量改变不需要重启App,甚至不需要重启当前的Activity,修改一个现有方法中的代码时会采用Hot Swap
-
Warm Swap
App不需重启,但Activity需要重启,修改或删除一个现有的资源文件会采用Warm Swap
-
Cold Swap
App需要重启,但不需要重新安装,采用Cold Swap的情况很多,比如添加、删除或修改一个字段和方法
-
-
原理
- 创建新的
AssetManager
,通过反射调用addAssetPath
方法加载外部的资源,这样创建的AssetManager
就含有了外部资源 - 将
AssetManager
类型的mAssets
字段的引用全部替换为创建新的AssetManager
- 创建新的
-
-
代码修复
代码修复主要有三个方案,分别是类加载方案、底层替换方案和Instant Run方案
-
类加载方案
类加载方案是基于Dex分包方案,
-
Dex分包
Dex分包主要是将应用代码分成多个dex文件,将应用启动时必须用到的类和这些类的直接引用类放到主Dex中,其他代码放到其他Dex中,从而解决Dex的65536问题和LinearAlloc限制。
Dex分包方案主要有两种,分别是Google官方方案、Dex自动拆包及动态加载方案
类加载方案需要重启App后让ClassLoader重新加载新的类,因为类是无法被卸载的,要想重新加载新的类就需要重启App,因此采用类加载方案的热修复框架不能及时生效,采用类加载方案的的框架其实现方式也是有区别的,如QQ空间的超级补丁和Nuwa是按照将补丁包放在Element数组的第一个元素得到优先加载;微信Tinker将新旧Apk做diff,得到patch.dex,再将patch.dex与手机中APK的classes.dex做合并,生成新的classes.dex,然后在运行包时通过反射将classes.dex放在Element数组的第一个元素;饿了么的Amigo则是将补丁包中每个dex对应的Element取出来,之后组成新的Element数组,在运行时通过反射用新的Element数组替换掉现有的Element数组。Element数组指的是DexPathList的findClass方法中用的dexElements
/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java /** * Finds the named class in one of the dex files pointed at by * this instance. This will find the one in the earliest listed * path element. If the class is found but has not yet been * defined, then this method will define it in the defining * context that this instance was constructed with. * * @param name of class to find * @param suppressed exceptions encountered whilst finding the class * @return the named class or {@code null} if the class is not * found in any of the dex files */ public Class<?> findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { Class<?> clazz = element.findClass(name, definingContext, suppressed); if (clazz != null) { return clazz; } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; }
-
-
底层替换方案
底层替换方案不会再次加载新类,而是直接在Native层修改原有类,但在原有类上进行修改限制会比较多,不能增减原有类的方法和字段,底层替换方案可以立即生效不用重启。
底层方案主要是替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体
AndFix采用的是替换ArtMethod结构体中的字段,这样会有兼容问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败;Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。
ArtMethod结构体中包含了Java方法的所有信息,包括执行入口,访问权限、所属类和代码执行地址等
-
Instant Run方案
使用ASM在每一个方法都注入一些代码
ASM:是一个Java字节码操作框架,它能够动态生成类或者增强现有类的功能。ASM可以直接产生class文件,也可以在类被加载到虚拟机前动态改变类的行为
-
-
动态链接库的修复
主要有两种方案:
- 将so补丁插入到
NativeLibraryElement
数组的前部,让so补丁的路径先被返回和加载 - 调用System的load方法来接管so的加载入口
- 将so补丁插入到