概述
任何一个Java程序都有一个或多个class,程序运行时,需要将class文件加载到JVM中才可以使用,负责加载class文件的就是ClassLoader。每一个Class对象内部都有一个classLoader字段来标记该对象应该由哪一个ClassLoader加载。
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
...
/** defining class loader, or null for the "bootstrap" system loader. */
private transient ClassLoader classLoader;
代码中的ClassLoader就是本文要概述的类加载器。
分类
ClassLoader是抽象类,Android使用最多的ClassLoader实现类有三种:BootClassLoader,PathClassLoader,DexClassLoader。
public abstract class ClassLoader {
static private class SystemClassLoader {
public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
- BootClassLoader
private static class BootClassLoader extends BuiltinClassLoader {
BootClassLoader(URLClassPath bcp) {
super((String)null, (BuiltinClassLoader)null, bcp);
}
protected Class<?> loadClassOrNull(String cn) {
return ClassLoaders.JLA.findBootstrapClassOrNull(this, cn);
}
}
加载Android Framework中的class文件。
测试:
Log.d(TAG, "Activity的ClassLoader:" + Activity.class.getClassLoader().toString());
结果:
Activity的ClassLoader:java.lang.BootClassLoader@93afdaf
- PathClassLoader
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 PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
加载Android应用程序类,可以指定dex,以及jar、zip、apk中的classes.dex文件。
测试:
Log.d(TAG, "AppCompatActivity的ClassLoader:" + AppCompatActivity.class.getClassLoader().toString());
结果:
AppCompatActivity的ClassLoader:dalvik.system.PathClassLoader[DexPathList[
说明AppCompatActivity并不属于Framework。
- DexClassLoader
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!");
}
}
optimizedDirectory:dexopt的优化文件odex的产出目录,为null时的默认路径为:/data/dalvik-cache。
DexClassLoader加载指定的dex,以及jar、zip、apk中的classes.dex文件,多用于热修复和插件化。
联系
PathClassLoader和DexClassLoader都继承自BaseDexClassLoader。两者的唯一区别是DexClassLoader多个一个optimizedDirectory参数,并且会将其创建的File对象传递给super。两者都可以加载指定dex,以及jar、zip、apk中的classes.dex文件。
流程
- 创建DexPathList 对象pathList,同时将dex文件加载到内存中,生成DexFile对象,再生成Element,并添加到Element数组dexElements。
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent, boolean isTrusted) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
if (reporter != null) {
reportClassLoaderChain();
}
}
- BaseDexClassLoader调用findClass方法。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
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;
}
- 调用DexPathList 的findClass()方法。
public Class<?> findClass(String name, List<Throwable> 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;
}
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
遍历dexElements里面的元素,通过遍历所有加载过的dex文件,调用loadClassBinaryName方法寻找想要的类,如果有就直接返回。
双亲委托机制
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 首先,检查class是否已被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
// 如果父类已加载,则使用父类进行加载
c = parent.loadClass(name, false);
} else {
// 如果父类为null,则使用findBootstrapClassOrNull进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
// 如果没有发现,就自己findclass
c = findClass(name);
}
}
return c;
}
如果父类为null时,其实使用findBootstrapClassOrNull加载其实相当于什么也没做。
private Class<?> findBootstrapClassOrNull(String name)
{
return null;
}
类加载器在加载类时候,会采用双亲委托机制。首先将加载任务委托给父类进行加载,若有多级父类,则一次递归,若父类可以完成加载,则成功返回;如果父类没有办法完成加载任务就自己加载。