目录
1.1:Bootstrap ClassLoader(引导类加载器):
1.2:Extensions ClassLoader(拓展类加载器):
1.3:Application ClassLoader(应用程序类加载器):
Java中的ClassLoader:
Java的类加载器,它的主要作用就是通过多种类加载器来查找和加载class文件到Java虚拟机中。
1.ClassLoader的类型:
Java的类加载器主要有两种类型,即系统类加载器和自定义类加载器。其中系统类加载器包括3种,分别是:Bootstrap ClassLoader(引导类加载器),Extensions ClassLoader(拓展类加载器),Application ClassLoader(应用程序类加载器)。
1.1:Bootstrap ClassLoader(引导类加载器):
用C/C++代码实现的加载器,用于加载指定的JDK的核心类库。Java虚拟机的启动就是通过引导类加载器创建一个初始类来完成的。该加载器不能被Java代码访问到,但是可以查询某个类是否被引导类加载器加载过。需要注意的是Bootstrap ClassLoader并不是继承java.lang.ClassLoader。它可以加载:
- $JAVA_HOME/jre/lib目录。
- -Xbootclasspath指定的目录。
1.2:Extensions ClassLoader(拓展类加载器):
Java中的实现类为ExtClassLoader,用于加载Java的拓展类,提供除了系统类之外的额外功能。ExtClassLoader可以用来加载:
- 加载$JAVA_HOME/jre/lib/ext/目录。
- 系统属性java.ext.dir所指定的目录。
1.3:Application ClassLoader(应用程序类加载器):
Java中的实现类为AppClassLoader,也叫做系统类加载器(System ClassLoader),这个类加载器可以通过ClassLoader的getSystemClassLoader方法获取到。它可以加载:
- 当前程序的classpath目录。
- 系统属性java.class.path指定的目录。
1.4:Custom ClassLoader:
除了系统提供的类加载器外,我们还可以自定义类加载器,自定义类加载器通过继承java.lang.ClassLoader类的方式来实现自己的类加载器。
2.ClassLoader的继承关系:
可以看到有5个类,其中:
- ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。
- SecureClassLoader继承了抽象类ClassLoader,但是SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类并且加入了权限方面的功能,加强了ClassLoader的安全性。
- URLClassLoader继承SecureClassLoader,可以通过URL路径从jar文件和文件夹加载类和资源。
- ExtClassLoader和AppClassLoader都是继承自URLClassLoader,它们都是Launcher的内部类,Launcher是Java虚拟机的入口应用,两者都是在Launcher中初始化的。
3.双亲委托模式:
类加载器查找Class采用的是双亲委托模式,所谓的双亲委托模式就是自定义类加载器首先从缓存中查找该class文件是否已经加载,如果没有,则不是自身去查找,而是委托给父加载器进行查找,这样依次递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该class,就会直接返回,如果在缓存没有找到,则开始在目录寻找,如果没有找到,继续依次向下查找,如果还是没有找到则最后会交给自己目录去查找。
3.1:总结:
- 自定义类加载器首先从缓存中查找class文件是否已经加载,如果已经加载就返回,否则委托给父加载器,如果一直没有加载到,则一直委托到Bootstrap ClassLoader,如果Bootstrap ClassLoader在缓存都没有找到,那么则在它的目录查找,如果找到就返回class,没有就交给子加载器,子加载器重复该步骤查找目录,直到返回至自定义类加载器,如果还没有就抛出异常。
- 采用双亲委托模式可以避免重复加载且更加安全。
4.自定义ClassLoader:
系统提供的类加载器只能够加载指定目录下的jar包和Class文件,如果想要加载网络上或者D盘某一个文件中的jar包和Class文件则需要自定义ClassLoader。实现自定义ClassLoader需要如下两个步骤:
- 定义一个自定义ClassLoader并且继承抽象类ClassLoader。
- 复写findClass方法,并且在findClass方法中调用defineClass方法。
Android中的ClassLoader:
Android中的ClassLoader和Java中的ClassLoader是有区别的。
1.ClassLoader的类型:
与Java中的ClassLoader加载的jar文件和class文件不同的是,Android中的DVM还是ART都是加载dex文件的。Android中的ClassLoader也分为两种类型,分别是系统类加载器和自定义类加载器。其中系统类加载器主要包括3种,分别是:BootClassLoader,Path ClassLoader和Dex ClassLoader。
1.1:BootClassLoader:
在Android系统启动时会使用BootClassLoader来预加载常用的类,与SDK中的BootstrapClassLoader不同的是,它并不是采用C/C++实现的,而是由Java实现的。BootClassLoader是ClassLoader的内部类,并且继承自ClassLoader,且它是一个单例类,而且只有在同一个包中才可以访问的,因此我们在应用程序中是无法调用的。
1.2:Dex ClassLoader:
Dex ClassLoader继承自BaseDexClassLoader,方法都在BaseDexClassLoader体现。Dex ClassLoader可以加载dex文件以及包含dex的压缩文件(例如:apk文件和jar文件),不管是加载哪一种文件,最终都是要加载dex文件的。Dex ClassLoader的构造方法有4个参数,分别是:
- dexpath:dex相关文件路径的集合,多个路径使用文件分隔符分隔,默认是:。
- optimizedDirectory:解压的dex文件的存储路径,必须是一个内部存储路径。
- librarySearchPath:包含C/C++库的路径集合。
- parent:父加载器。
1.3:Path ClassLoader:
Path ClassLoader继承自BaseDexClassLoader,方法都在BaseDexClassLoader体现。Android系统使用Path ClassLoader来加载系统类和应用程序的类,在Path ClassLoader中没有optimizedDirectory参数,这是因为Path ClassLoader已经默认了optimizedDirectory参数的值为:/data/dalvik-cache。所以Path ClassLoader无法定义解压的dex文件的存储路径,因此Path ClassLoader经常用来加载已经安装的apk的dex文件。
2.ClassLoader的继承关系:
在运行一个应用程序时需要用到两种类加载器,分别是:BootClassLoader和Path ClassLoader。其中Path ClassLoader的DexPathList中包含了很多的apk的路径,DexPathList就是在BaseDexClassLoader的构造方法中被创建的,里面存储了dex相关文件的路径,其中ClassLoader在执行双亲委托模式查找流程时会从DexPathList中进行查找。
图中一共有8个与ClassLoader相关的类,下面简单介绍一些类:
- ClassLoader 是一个抽象类,其中定义了ClassLoader的主要功能。BootClassLoader是它的内部类。
- SecureClassLoader类和JDK8中的 SecureClassLoader类的代码是一样的,它维承了抽象类ClassLoader。 SecureClassLoader并不是ClassLoader的实现类,而是拓展了 方法,ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
- URIClassLoader类和JDK 8中的URLClassLoader类的代码是一样的,它继承自SecureClassLoader,用来通过URL路径从jar文件和文件夹中加载类和资源。
- InMemoryDexClassLoader是Android 8.0 新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件。
- BaseDexClassLoader 继承自ClassLoader,是抽象类ClassLoader的具体实现类。PathClassLoader、DexClassLoader和 InMemoryDexClassLoader都继承自它。
3.ClassLoader的加载过程:
Android的ClassLoader同样采用了双亲委托模式,ClassLoader的加载方法为loadClass方法,这个方法被定义在抽象类ClassLoader中。ClassLoader的loadClass方法判断传入的类是否已经加载,如果没有就调用父加载器的loadClass方法,有就返回。同样如果委托过程没有找出来,就调用findClass方法进行查找流程。findClass方法需要由子类BaseDexClassLoader实现。在BaseDexClassLoader的构造方法中创建了DexPathList,并且后续调用了DexpathList的findClass方法。
DexpathList的findClass方法:通过遍历Element数组dexElement,然后调用Element的findClass方法,其方法内部会调用DexFile的loadClassBinaryName方法,DexFile的loadClassBinaryName方法中调用了defindClass方法,defindClass方法又调用了defineClassNative方法来执行查找流程,如果在Element(dex文件)找到了该类就返回,如果没有找到就接着在下一个Element进行查找。其中Element是DexPathList的静态内部类,内部封装了Dexfile,它用于加载dex。
4.BootClassLoader的创建:
BootClassLoader是什么时候被创建的呢?答案要从系统启动的Zygote进程启动开始说起,ZygoteInit的main方法通过preload方法预加载类和资源,preload方法中调用了ZygoteInit的preloadClasses方法,preloadClasses方法用于Zygote进程初始化时预加载常用类,该方法把PRELOADED_CLASSES的值是/system/etc/proloaded_classes文件封装为FileInputStream。该文件存有预加载类的目录。预加载是属于拿空间换时间的策略。接着把FileInputStream封装为BufferedReader,并且后续遍历BufferedReader,读出所有预加载类的名称,每读出一个就调用Class.forName方法加载该类。而在Class.forName方法中创建了BootClassLoader,它是用来加载该文件的预加载类的。
5.Path ClassLoader的创建:
Path ClassLoader的创建也是从Zygote进程开始说起,Zygote进程启动SystemServer进程时调用ZygoteInit的startSystemServer方法,快进到handleSystemServerProcess方法,该方法调用了createPathClassLoader方法创建了PathClassLoader。具体是createPathClassLoader方法中调用了PathClassLoaderFactory的createClassLoader方法利用工厂模式创建PathClassLoader。