1. 热修复解决方案
热补丁方案有很多,比较有名是有腾讯Tinker、阿里的AndFix、美团的RoBust以及QZone的超级补丁方案。
2. AndFix热修复
在native层动态替换java层的方法,通过native层hook java层的代码。
AndFix为什么能即时生效?
答:下次加载的时候会加载补丁包中的对应的方法,不会再调用原来有bug的方法。
3. Robust
对每个函数都在编译打包阶段自动的插入了一段代码,类似于代理,将方法执行的代码重定向到其他方法中。
//编写的代码
@Modify //改动代码后手动添加注解用于补丁包生成
public long getIndex() {
return 100;
}
//经过插桩后时间执行的代码
public long getIndex() {
if (changeQuickRedirect != null) {
return 修复的实现;
}
return 100L;
}
4. Tinker
Tinker通过计算对比指定的Bask Apk中的dex与修改后的Apk中的dex的区别,补丁包中的内容即为两者差分的描述。运行时将Base Apk中的dex与补丁包进行合成,重启后加载全新的合成后的dex文件。
5. Qzone
Qzone与Tinker比较类似,与Tinker不同的是,Tinker是一个patch.dex差分包,并且需要下载下来进行合成一个类,而QZone就是一个完整修复的类,不需要合成。
6. ClassLoader
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
-
BootClassLoader
用于加载Android Framework层class文件 -
PathClassLoader
用于Android应用程序类加载器。可以加载指定的dex,以及jar、zip、apk中的classes.dex -
DexClassLoader
加载指定的dex,以及jar、zip、apk中的classes.dex
PathClassLoader与DexClassLoader的区别:只是DexClassLoader构造函数中多了一个参数optimizedDirectory,用于指定特定的路径(app私有路径/data/data/packageName)存放odex,即多了一个dexopt优化流程,对dex文件进行验证和优化为odex(Optimized dex)文件,而PathClassLoader存放odex是放在默认路径下/data/davlik-cache
ART会执行AOT,但针对Dalvik开发的应用也能在ART环境中运作
- dexopt
对dex文件进行验证和优化为odex(Optimized dex)文件 - dexAot
在安装时对dex文件执行dexopt优化之后再将odex进行AOT提前编译操作,编译为OAT可执行文件(机器码)。
7. 双亲委托机制
某个类加载器在加载类时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务或者没有父类加载器时,才自己去加载。
-
避免重复加载,当父类加载器已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
-
安全性考虑,防止核心API库被随意篡改。
为什么要这样设计?
答:应用启动的就会创建BootClassLoader来加载framework中所有的类,而自己写的Activity代码BootClassLoader是没有办法加载的,它只能由PathClassLoader进行加载,因为自己写的Activity类它在apk中,而不在系统中。因此需要PathClassLoader来加载apk中的类。假如没有双亲委派机制,自己写的MainActivity需要继承AppCompatActivity(它属于framwork中的),framework中的类被BootClassLoad进行加载了,那PathClassLoader加载framework中的类就需要加载2次(apk中的类和framework中的类),其实framework中的类已经被BootClassLoad加载了,因此构成这样的一个双亲委派机制可以避免重复加载。
8. 类查找流程
9. 热修复流程
- 获取到当前应用的PathClassloader;
- 反射获取到DexPathList属性对象的pathList;
- 反射修改pathList的dexElements
- 把补丁包patch.dex转化为Element[] (patch)
- 获得patchList的dexElements属性(old)
- patch + dexElements合并,并反射赋值给pathList的dexElements
dexElements是什么?
答:是一个Element数组,Element中包含一个DexFile
DexFile就代表一个Dex文件,里面的native(C/C++)函数来进行Dex的加载工作