You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
publicClass<?> loadClass(Stringname) throwsClassNotFoundException {
returnloadClass(name, false);
}
protectedClass<?> loadClass(Stringname, booleanresolve)
throwsClassNotFoundException
{
// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundExceptione) {
// ClassNotFoundException thrown if class not found// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order// to find the class.c = findClass(name);
}
}
returnc;
}
Android plug-in and hotfix summary
[TOC]
插件化和热修复总述
首先明确一点,两者不是同一个概念。虽然在技术实现的角度来说,他们都是从系统加载起的角度出发,无论是 hook ,或者代理还是其他底层方式实现,都是通过 欺骗 Android 系统的方式,让宿主正常的加载和运行插件(补丁)中的内容;
但是两者的出发点不同。插件化,更多是想把需要实现的模块或者功能当作一个独立模块提取出来,较少宿主的大小,当需要使用到相应的功能时再去加载相应的模块。热修复则是从修复 bug 出发,强调的是在不需要二次安装应用的前提下修复已知的 bug。
宿主:当前运行的 APP
插件:相对于插件化技术来说,就是要加载运行的 apk类文件
补丁:对于热修复技术来说,就是要加载运行的 .patch, .dex 等一系列包含 dex 修复内容的文件。
类加载机制
双亲委派模型
在加载一个字节码文件时,会询问当前的classLoader是否已经加载过此字节码文件。如果加载过,则直接返回,不再重复加载。如果没有加载过,则会询问它的Parent是否已经加载过此字节码文件,同样的,如果已经加载过,就直接返回parent加载过的字节码文件,而如果整个继承线路上的classLoader都没有加载过,才由child类加载器(即,当前的子classLoader)执行类的加载工作。
验证多个类是同一个类的成立条件:
loadClass()
通过loadClass()这个方法来验证双亲委派模型
找到ClassLoader这个类中的loadClass()方法,它调用的是另一个2个参数的重载loadClass()方法。
可以看到,如前面所说,加载一个类时,会有如下3步:
Android 中的 ClassLoader
类加载器类型
Android跟java有很大的渊源,基于jvm的java应用是通过ClassLoader来加载应用中的class的,Android对jvm优化过,使用的是dalvik虚拟机,且class文件会被打包进一个dex文件中,底层虚拟机有所不同,那么它们的类加载器当然也是会有所区别。
Android中最主要的类加载器有如下4个:
BootClassLoader:加载Android Framework层中的class字节码文件(类似java的Bootstrap ClassLoader)
PathClassLoader:加载已经安装到系统中的Apk的class字节码文件(类似java的App ClassLoader)
DexClassLoader:加载指定目录的class字节码文件(类似java中的Custom ClassLoader)
BaseDexClassLoader:PathClassLoader和DexClassLoader的父类
上面代码中可以通过上下文拿到当前类的类加载器(PathClassLoader),然后通过getParent()得到父类加载器(BootClassLoader),这是由于Android中的类加载器和java类加载器一样使用的是双亲委派模型。
PathClassLoader与DexClassLoader的区别
使用场景
PathClassLoader:只能加载已经安装到Android系统中的apk文件(/data/app目录),是Android默认使用的类加载器。
DexClassLoader:可以加载任意目录下的dex/jar/apk/zip文件,比PathClassLoader更灵活,是实现热修复的重点。
代码差异
2个结论:
PathClassLoader与DexClassLoader都继承于BaseDexClassLoader。
PathClassLoader与DexClassLoader在构造函数中都调用了父类的构造函数,但DexClassLoader多传了一个optimizedDirectory。
BaseDexClassLoader
通过观察PathClassLoader与DexClassLoader的源码我们就可以确定,真正有意义的处理逻辑肯定在BaseDexClassLoader中,所以下面着重分析BaseDexClassLoader源码。
构造函数
dexPath:要加载的程序文件(一般是dex文件,也可以是jar/apk/zip文件)所在目录。
optimizedDirectory:dex文件的输出目录(因为在加载jar/apk/zip等压缩格式的程序文件时会解压出其中的dex文件,该目录就是专门用于存放这些被解压出来的dex文件的)。
libraryPath:加载程序文件时需要用到的库路径。
parent:父加载器
从一个完整App的角度来说,程序文件指定的就是apk包中的classes.dex文件;但从热修复的角度来看,程序文件指的是补丁。
findClass()
DexPathList
构造函数
这个构造函数中,保存了当前的类加载器definingContext,并调用了makeDexElements()得到Element集合。
在这个方法中,看到了一些眉目,总体来说,DexPathList的构造函数是将一个个的程序文件(可能是dex、apk、jar、zip)封装成一个个Element对象,最后添加到Element集合中。
findClass()
热修复的实现原理
经过对PathClassLoader、DexClassLoader、BaseDexClassLoader、DexPathList的分析,我们知道,安卓的类加载器在加载一个类时会先从自身DexPathList对象中的Element数组中获取(Element[] dexElements)到对应的类,之后再加载。采用的是数组遍历的方式,不过注意,遍历出来的是一个个的dex文件。在for循环中,首先遍历出来的是dex文件,然后再是从dex文件中获取class,所以,我们只要让修复好的class打包成一个dex文件,放于Element数组的第一个元素,这样就能保证获取到的class是最新修复好的class了(当然,有bug的class也是存在的,不过是放在了Element数组的最后一个元素中,所以没有机会被拿到而已。
The text was updated successfully, but these errors were encountered: