1.Java中的ClassLoader
1.1 CLassLoader的类型
Java的类加载器主要有两种类型,即系统类加载器和自定义类加载器。系统类加载器包括3中,分别是Bootstrap ClassLoader,Extensions ClassLoader和Application ClassLoader。
Bootstrap ClassLoader(引导类加载器)
C/C++代码实现的加载器,用于加载指定的JDK的核心类库,比如java.lang.等系统类。用来加载一下目录的类库:
- %JAVA_HOME%/jre/lib目录
- -Xbootclasspath参数指定的目录
Java虚拟机的启动就是通过Bootstrap ClassLoader创建一个初始类来完成的。由于Bootstrap ClassLoader使用C/C++实现的,不能被Java代码访问到。Bootstrap ClassLoader也不是继承java.lang.ClassLoader。可以通过如下代码得出Bootstrap ClassLoader加载的目录:
System.out.println(System.getProperty("sun.boot.class.path"));
Extensions ClassLoader(拓展类加载器)
Java中类对应是ExtClassLoader,用于加载Java的拓展类,提供除了系统类之外的额外功能,加载以下目录的类库:
- %JAVA_HOME%/jre/lib/ext
- 系统属性java.ext.dir所指定的目录
通过以下代码可以加载Extensions ClassLoader加载目录:
System.out.println(System.getProperty("java.ext.dirs"));
Application ClassLoader(应用程序类加载器)
Java中实现类为AppClassLoader,可以通过ClassLoader的getSystemClassLoader获取到。用来加载以下目录的类库:
- 当前程序的ClassPath目录
- 系统属性java.class.path指定的目录
Custom ClassLoader(自定义类加载器)
通过继承java.lang.ClassLoader来实现自定义的类加载器。ExtClassLoader和AppClassLoader也是继承了java.lang.ClassLoader类。
1.2 CLassLoader的继承关系
ClassLoader的继承关系如下所示:
- ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。
- SecureClassLoader继承了抽象类ClassLoader,但SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
- URLClassLoader继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
- ExtClassLoader和AppClassLoader都继承自URLClassLoader,它们都是Launcher
的内部类,Launcher - 是Java虚拟机的入口应用,ExtClassLoader和AppClassLoader都是在Launcher中进行初始化的。
1.3 双亲委托模式
类加载器查找Class所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该Class是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行查找,这样依次的进行递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找。加载过程如下图所示:
这种加载方式可以从源码中分析:
public abstract class ClassLoader {
...
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name); //1
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false); //2
} else {
c = findBootstrapClassOrNull(name); //3
}
} catch (ClassNotFoundException e) {
// 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);
}
}
return c;
}
...
}
注释1处用来检查类是否已经加载,如果已经加载则后面的代码不会执行,最后会返回该类。没有加载则会接着向下执行。
注释2处,如果父类加载器不为null,则调用父类加载器的loadClass方法。如果父类加载器为null则调用注释3处的findBootstrapClassOrNull方法,这个方法内部调用了Native方法findBootstrapClass,findBootstrapClass方法中最终会用Bootstrap Classloader来查找类。如果Bootstrap Classloader仍没有找到该类,也就说明向上委托没有找到该类,则调用注释4处的findClass方法继续向下进行查找。
双亲委托模式的好处
1.避免重复加载,如果已经加载过一次Class,就不需要再次加载,而是先从缓存中直接读取。
2.更加安全,如果不使用双亲委托模式,就可以自定义一个String类来替代系统的String类,这显然会造成安全隐患,采用双亲委托模式会使得系统的String类在Java虚拟机启动时就被加载,也就无法自定义String类来替代系统的String类,除非我们修改类加载器搜索类的默认算法。还有一点,只有两个类名一致并且被同一个类加载器加载的类,Java虚拟机才会认为它们是同一个类。
2.Android中的ClassLoader
2.1 ClassLoader的类型
Java中的ClassLoader是加载Class文件,而Android中的ClassLoader是加载dex文件,所以两者并不相同。
Android中的ClassLoader包括系统类加载器和自定义类加载器。系统类加载器包含:BootClassLoader、PathClassLoader和DexClassLoader。
BootClassLoader
BootClassLoader是ClassLoader的内部类,继承自ClassLoader,它的访问修饰符默认的,只有在同一个包中才可以访问,因此我们在应用程序中是无法直接调用的
PathClassLoader
PathClassLoader用来加载系统类和应用程序的类。
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
PathClassLoader继承自BaseDexClassLoader,实现也都在BaseDexClassLoader中。PathClassLoader的构造方法中没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的值为:/data/dalvik-cache,很显然PathClassLoader无法定义解压的dex文件存储路径,因此PathClassLoader通常用来加载已经安装的apk的dex文件(安装的apk的dex文件会存储在/data/dalvik-cache中)。
DexClassLoader
DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),不管是加载哪种文件,最终都是要加载dex文件,为了方便理解和叙述,将dex文件以及包含dex的压缩文件统称为dex相关文件。
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
DexClassLoader的构造方法有四个参数:
dexPath:dex相关文件路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’
optimizedDirectory:解压的dex文件存储路径,这个路径必须是一个内部存储路径,一般情况下使用当前应用程序的私有路径:/data/data//…。不过在Android8.0及以上的源码中,在BaseDexClassLoader中可以看到,这个参数已被取消并且无效。
librarySearchPath:包含 C/C++ 库的路径集合,多个路径用文件分隔符分隔分割,可以为null。
parent:父加载器。
在android8.0以上,optimizedDirectory可以不传,所以PathClassLoader和DexClassLoader均可以用来加载外部apk(如果要访问sd卡,注意添加权限)。
2.1 ClassLoader的继承关系
Android中ClassLoader的继承关系如下所示:
- ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。BootClassLoader是它的内部类。
- SecureClassLoader类和JDK8中的SecureClassLoader类的代码是一样的,它继承了抽象类ClassLoader。SecureClassLoader并不是
- ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
- URLClassLoader类和JDK8中的URLClassLoader类的代码是一样的,它继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
- InMemoryDexClassLoader是Android8.0新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件。
- BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoader和DexClassLoader都继承它。
总结
本篇文章分析了Java的ClassLoader的类型以及继承关系以及Android的ClassLoader的类型以及继承关系。
参考链接:
https://blog.csdn.net/itachi85/article/details/78088701
https://blog.csdn.net/itachi85/article/details/78276837