概述
一个ClassLoader对象负责加载类。ClassLoader是一个抽象类。当给定一个类的二进制名称时,ClassLoader会尝试 查找 或 生成 构成该类定义的数据(Class对象)。一种典型的策略是将 类名 转换成文件名,然后从文件系统中读取该名称的类文件。
每一个Class对象都包含一个定义它的ClassLoader对象。 Class#getClassLoader()
数组类的类对象不是被ClassLoader创建,而是根据Java运行时的要求自动创建的。数组类的ClassLoader和其元素类型的ClassLoader相同,如果元素类型是原始类型,则数组类没有ClassLoader。
ClassLoader类使用委托模型搜索 类和资源,每一个ClassLoader对象都有一个关联的父ClassLoader。当查找 类和资源 时,ClassLoader实例首先会将对 类和资源 的搜索委托给其 父ClassLoader,然后再尝试自己查找 类和资源。BootClassLoader是虚拟机内置的ClassLoader,本身没有 父ClassLoader,可以作为其他ClassLoader实例的 parent。
通常情况下,Java虚拟机会以平台相关的方式从本地文件系统加载类,但是有 类 不是源自文件的,可能有其它来源,例如网络,也可能由应用程序构造。ClassLoader#defineClass
可以将字节数组转换成Class对象。
类的二进制名称:
java.lang.Strinng
javax.swing.JSpinner$DefaultEditor
java.net.URLClassLoader$3$1
ClassLoaderUML类图
ClassLoader代码
public abstract class ClassLoader {
...
public Class> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} 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;
}
/**
* Finds the class with the specified binary name.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass loadClass} method after checking the
* parent class loader for the requested class. The default implementation
* throws a ClassNotFoundException.
*
* @param name
* The binary name of the class
*
* @return The resulting Class object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
...
}
重点看 loadClass
的方法实现,使用了 Template Method 设计模式。定义了查找类的主体算法逻辑, findClass
可以不同具体实现。例如可以从文件、网络、内存中寻找。loadClass
主体逻辑很简单:
1、查询 类 会否已加载(从自己ClassLoader中查找),如果已加载直接返回。
2、如果未加载,委托给 父ClassLoader 去查询。父ClassLoader又会 执行一遍同样的 loadClass
逻辑
3、如果从来都没有加载过,会执行 findClass
去创建 Class 对象。
BaseDexClassLoader代码
是一个基于dex的 一个常用功能的ClassLoader实现的 基类
还是看原文吧,怎么翻译都很怪异。
Base class for common functionality between various dex-based ClassLoader implementations.
public class BaseDexClassLoader extends ClassLoader {
...
private final DexPathList pathList;
/**
* Constructs an instance.
* Note that all the *.jar and *.apk files from {@code dexPath} might be
* first extracted in-memory before the code is loaded. This can be avoided
* by passing raw dex files (*.dex) in the {@code dexPath}.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android.
* @param optimizedDirectory this parameter is deprecated and has no effect
* @param librarySearchPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
if (reporter != null) {
reporter.report(this.pathList.getDexPaths());
}
}
//dexFile must be an in-memory representation of a full dexFile.
public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
// TODO We should support giving this a library search path maybe.
super(parent);
this.pathList = new DexPathList(this, dexFiles);
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
List suppressedExceptions = new ArrayList();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
...
}
BaseDexClassLoader#findClass
的最终的实现是 DexPathList #findClass
DexPathList代码
final class DexPathList {
...
private final ClassLoader definingContext;
/**
* List of dex/resource (class path) elements.
* Should be called pathElements, but the Facebook app uses reflection
* to modify 'dexElements' (http://b/7726934).
*/
private Element[] dexElements;
/** List of native library path elements. */
private final NativeLibraryElement[] nativeLibraryPathElements;
/** List of application native library directories. */
private final List nativeLibraryDirectories;
/** List of system native library directories. */
private final List systemNativeLibraryDirectories;
//重点
public Class> findClass(String name, List suppressed) {
for (Element element : dexElements) {
Class> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
}
static class Element {
/**
* A file denoting a zip file (in case of a resource jar or a dex jar), or a directory
* (only when dexFile is null).
*/
private final File path;
private final DexFile dexFile;
private ClassPathURLStreamHandler urlHandler;
private boolean initialized;
...
public Class> findClass(String name, ClassLoader definingContext,
List suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
...
}
DexPathList 中维护了一个数组 Element[] dexElements
,这个数组是一个dex资源的集合。(注意看dexElements 的注释,很有意思) findClass
的时候,遍历 dexElements
寻找Class,如果找到就返回,否则继续循环,如果最终没找到哦,则返回null。也就是说同一个类,如果两个dex文件都包含,则先取到哪个dex文件,就从这个文件中装载,热修复就可以从这里入手,修复异常的java类
。
其它
PathClassLoader 和 DexClassLoader 没有具体逻辑实现,没看出有什么区别。
加载系统framework的类使用的 BootClassLoader、加载应用自己编写的类用的 PathClassLoader
Log.e("MainActivity", "" + String.class.getClassLoader());
Log.e("MainActivity", "" + MainActivity.class.getClassLoader());
2020-05-08 15:36:02.782 31478-31478/com.jxf.androidhotfix E/MainActivity: java.lang.BootClassLoader@8046010
2020-05-08 15:36:02.783 31478-31478/com.jxf.androidhotfix E/MainActivity: dalvik.system.PathClassLoader[DexPathList[[zip file "/sdcard/patch.jar", zip file "/data/app/com.test.androidhotfix-da67YM0aVlq3wyo_3rK_rw==/base.apk"],nativeLibraryDirectories=[/data/app/com.test.androidhotfix-da67YM0aVlq3wyo_3rK_rw==/lib/arm64, /system/lib64, /system/vendor/lib64]]]