1、ClassLoader简介
在应用程序打包成APK时,程序中所创建的类、导入和引用的类都会被编译打包成一个或多个的dex文件中,打包成的dex文件在使用中如何加载类?答案就在本篇主题ClassLoader中,ClassLoader从字面就可以知道它主要用于类的加载,当代码中需要调用某个类时,ClassLoader就会遍历所有的dex文件中的类并保存在集合中,然后从集合中加载指定的Class文件,最终转换成JVM中使用的类;
2、ClassLoader工作机制
- Android中ClassLoader分类
- PathClassLoader:负责加载系统和apk中的类,context.getClassLoader获取到的就是PathClassLoader实例;
- DexClassLoader:负责加载外部类(jar或apk),也用于热修复方案的执行;如:修复的dex文件;
- BaseDexClassLoader:PathClassLoader 和 DexClassLoader的父类,主要的执行逻辑和文件的处理都在其中(后面会分析它的源码);
- BootClassLoader:继承与ClassLoader类,一般来说向上传递时最顶层就是BootClassLoader,它在Zygote进程启动开始时,在ZygoteInit.main()方法中执行资源预加载,此时会单例创建BootClassLoader对象,它在loadClass中直接调用findClass(),而findClass()中调用Class.classForName(name, false, null)查找类;
- 获取ClassLoader的继承关系
var loader = classLoader
while (loader != null) {
System.out.println(loader)
loader = loader.parent
}
- 获取输出的结果:PathClassLoader中parent为BootClassLoader,在PathClassLoader内部保存这base.apk和library的文件
2019-08-29 13:13:10.444 29022-29022/com.alex.kotlin.optimization I/System.out: dalvik.system.PathClassLoader
[DexPathList[
[zip file "/data/app/com.alex.kotlin.optimization-lLeC3751i-Krivn3eNgrYA==/base.apk"],
nativeLibraryDirectories=[/data/app/com.alex.kotlin.optimization-lLeC3751i-Krivn3eNgrYA==/lib/arm64, /system/lib64, /system/vendor/lib64]]]
2019-08-29 13:13:10.444 29022-29022/com.alex.kotlin.optimization I/System.out: java.lang.BootClassLoader@14d954f
- 源码继承关系
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
-
ClassLoader 传递性
虚拟机的加载策略:在触发虚拟机对类的加载时,虚拟机默认使用调用对象中的ClassLoader加载,而调用对象又被调用它的对象中的ClassLoader加载,按此传递最终执行到最上层的ClassLoader -
双亲委派机制
- 在每个ClassLoader中都持有其父类的引用,在加载类文件时,首先会判断当前ClassLoader中是否已经加载过此类,如果加载过从缓存中获取,否则调用其父类的加载器去查找,父类也会先检查自己的缓存然后再调用父类查找,直到调用到BootstrapClassLoader(它内部持有的父类为空),当BootstrapClassLoader没有找到会向下逐级触发子类ClassLoader的查找,直到发起者
- 双亲委托机制的好处:(1)先执行父类的查找,避免了同一个类的多次加载;(2)这种先从父类查找的机制,使程序无法修改和替代Java基础包中的类,提高程序的安全性
3、ClassLoader源码分析
- loadClass():ClassLoader中加载类时调用loadClass(),代码中将双亲机制体现的淋漓尽致
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
Class<?> c = findLoadedClass(name); //查找是否加载过此类
if