插件化和热修复的原理,都是动态加载 dex/apk 中的类/资源,让宿主正常的加载和运行插件(补丁)中的内容。 两者的目的不同。
插件化目标是想把需要实现的模块或功能当做一个独立的提取出来,减少宿主的规模。所以插件化重在解决组件的生命周期,以及资源的问题
热修复目标在修复已有的问题。重在解决替换已有的有问题的类/方法/资源等。
代码修复主要有三个方案,分别是底层替换方案、类加载方案和Instant Run方案。
类加载方案
Android中要加载某一个类,最终都是调动DexPathList中的findClass()方法
加载一个类的时候,都会去循环dexElements数组取出里面的dex文件,然后从dex文件中找目标类,只要目标类找到,则直接退出循环,也就是后面的dex文件就没有被取到的机会。 那我们就可以根据这样的一个原理,将修改后的类编译后统一放在patch.dex补丁文件中,通过反射将patch.dex放在dexElemets这个数组的第一个元素,这样,当加载出现bug的类时,首先会先从patch.dex这个文件中去找,因为我们将修改后的类放在了patch.dex文件中,所以肯定会被找到(此时加载到的是已经修复的类),一旦被找到,后面dex中的bug类就没办法被加载到。
QZone修复的步骤:
-
通过获取到当前应用的Classloader,即为BaseDexClassloader
-
通过反射获取到他的DexPathList属性对象pathList
-
通过反射调用pathList的dexElements方法把patch.dex转化为Element[]
-
两个Element[]进行合并,把patch.dex放到最前面去
-
加载Element[],达到修复目的
Tinker修复的流程:
微信的Tinker将新旧APK做了diff,得到path.dex,再将patch.dex与手机中APK的classes.dex做合并,生成新的classes.dex,然后在运行时通过反射将classes.dex放在Elements数组的第一个元素。
底层替换方案
在native层将传入的javaMethod在ART虚拟机中对应一个ArtMethod指针,ArtMethod结构体中包含了Java方法的所有信息,包括执行入口、访问权限、所属类和代码执行地址等。
替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体,这就是底层替换方案。
AndFix采用的是替换ArtMethod结构体中的字段,这样会有兼容性问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败。
Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。