原理
ClassLoader & Multidex
Java在运行时加载对应的类是通过ClassLoader来实现的,ClassLoader本身是一个抽象来,Android中使用PathClassLoader类作为Android的默认的类加载器, PathClassLoader其实实现的就是简单的从文件系统中加载类文件。PathClassLoade本身继承自BaseDexClassLoader,BaseDexClassLoader重写了findClass方法,该方法是ClassLoader的核心1
2
3
4
5
6
7
8
9
10
11
12
13
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
BaseDexClassLoader将findClass方法委托给了pathList对象的findClass方法,pathList对象是在BaseDexClassLoader的构造函数中new出来的, 它的类型是DexPathList。看下DexPathList.findClass源码是如何做的:
1 | public Class findClass(String name, List<Throwable> suppressed) { |
直接就是遍历dexElements列表,然后通过调用element.dexFile对象上的loadClassBinaryName方法来加载类,如果返回值不是null,就表示加载类成功,会将这个Class对象返回。 而dexElements对象是在DexPathList类的构造函数中完成初始化的。
1 | this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, |
总结:要实现热更新的关键是将Patch注入到ClassLoader里,并保证其先于原始的模块被加载。方法是通过Multidex实现分包,利用反射动态修改ClassLoader的属性
基于这一原理的方案有:HotFix(RocooFix)、Tinker、Nuwa
Native Hack
AndFix不同于QQ空间超级补丁技术和微信Tinker通过增加或替换整个DEX的方案,提供了一种运行时在Native修改Filed指针的方式,实现方法的替换,达到即时生效无需重启,对应用无性能消耗的目的。
原理图如下: