第十二章 理解 ClassLoader

何言 2022年03月07日 302次浏览

热修复和插件化是目前比较热门的技术,要想更好地掌握它 需要先了 ClassLoader。

12.1 Java 中的 ClassLoader

12.1.1 ClassLoader 的类型

image20220307153418678.png

1. Bootstrap ClassLoader

C/C++ 编写的加载器,用于加载 JDK 的核心类,如:java.lang.*,java.util.*,它会加载以下目录中的类库:

  • $JAVA_HOME/jre/lib 目录
  • -Xbootclasspath 参数指定的目录

Java 虚拟机的启动就是通过 Bootstrap ClassLoader 创建-个初始类来完成的。

2. Extensions ClassLoader

Java 中实现类为 ExtClassLoader,用于加载 Java 的扩展类,主要是以下目录中的类库:

  • 加载 $JAVA_HOME/jre/lib/ext目录
  • 系统数学 java.ext.dir 指定的目录

3. Application ClassLoader

Java 中实现类为 AppClassLoader,又可以称作 System ClassLoader,因为代码中可以通过 ClassLoader.getSystemClassLoader 方法获取。

会加载以下目录的类库:

  • 当前程序的 Classpath 目录
  • 系统属性 java.class.path 指定的目录

4. Custom ClassLoader

可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,之前的 ExtClassLoader 与 AppClassLoader 也是继承了该类。

12.1.2 ClassLoader 继承关系

image20220307154913548.png

  • ClassLoader 是一个抽象类,其中定义了 ClassLoader 的主要功能。
  • SecureClassLoader 继承了抽象类 ClassLoader,但本身无法加载类,只是扩展了 ClassLoader,加入了权限方面的功能。
  • URLClassLoader 继承自 SecureClassLoader,主要是可以通过 URL 路径从 jar 文件和文件夹中加载类。
  • ExtClassLoader 和 AppClassLoader 都继承自 URLClassLoader,他们都是 Launcher 的内部类。Launcher Java 虚拟机的人口应用, ExtClassLoader AppClassLoader 都是在 Launcher 中进行初始化的。

12.1.3 双亲委派

image20220307155224209.png

优势:

  • 避免重复加载
  • 更加安全

当然也有缺点,在 深入理解 Java 虚拟机中有更详细的说明。

12.1.4 自定义 ClassLoader

实现自定义 ClassLoader 需要如下步骤:

  1. 定义一个自定义的 ClassLoader 并继承(可间接继承)抽象类 ClassLoader。
  2. 复写 findClass 方法,并在其中调用 defineClass 方法。

12.2 Android 中的 ClassLoader

12.2.1 ClassLoader 的类型

image20220307155908591.png

1. BootClassLoader

与 Java 不同,Android 中的 BootClassLoader 是由 Java 实现的。BootClassLoader 是 ClassLoader 的内部类,并继承自 ClassLoader ,BootClassLoader 个单例类,需要注意的是 BootClassLoader 的访问修饰符是默认的,只有在同个包中才 可以访问,因此我们在应用程序中是无发直接调用的。

2. DexClassLoader

DexClassLoader 可以加载 dex 文件以及包含 dex 的压缩文件(apk 或 jar 文件)。

DexClassLoader 继承自 BaseDexClassLoader ,方法都在 BaseDexClassLoder 实现。

3. PathClassLoader

Android 系统使用 PathClassLoader 来加载系统类和应用程序的类。它继承自 BaseDexClassLoader,也都在 BaseDexClassLoader 中实现。

但构造方法中没有指定路径,实际上其内置了路径,一般为 /data/dalvik-cache,这里存储着安装的 apk 的 dex 文件。

12.2.2 ClassLoader 的继承关系

image20220307160701545.png

  • ClassLoader 是一个抽象类,定义了类加载器的主要功能。BootClassLoader 是其内部类。
  • SecureClassLoader 类和 JDK 8 中的 SecureClassLoader 类的代码一致。
  • URLClassLoader 类和 JDK 8 中的 URLClassLoader 类的代码一致。
  • InMemoryDexClassLoader 是 Android 8.0 新增的类加载器,继承于 BaseDexClassLoader,用于加载内存中的 dex 文件。
  • BaseDexClassLoader 继承自 ClassLoader,是 ClassLoader 的具体实现类。有三个子类。

12.2.3 ClassLoader 加载过程

其中 Element 为 DexPathList 的静态内部类,抽象出了一个类加载的实体。而 DexFile 用于加载某个 dex 文件。最终在 loadClassBinaryName 方法会调用 defineClass 方法,最终调用 defineClassNative 方法来加载文件,该方法为 native 方法。

image20220307202322406.png

12.2.4 BootClassLoader 的创建

BootClassLoader 是在 Zygote 进程的 Zygote 入口方法中被创建,用于加载 preloaded-classes 文件中存有的预加载类。

12.2.5 PathClassLoader 的创建

PathClassLoader 是在 SystemServer 进程中通过工厂模式创建的。

12.3 本章小结

  • Java 的引导类加载器是由 C++ 编写的,Android 中的引导类则是用 Java 编写的。
  • Android 的继承关系要比 Java 继承关系复杂,提供的功能更多。
  • 由于 Android 中加载的不再是 Class 文件,因此 Android 中没有 ExtClassLoader 和 AppClassLoader,而是使用了 PathClassLoader 和 DexClassLoader