类加载
image.pngpublic abstract class ClassLoader {
ClassLoader位于/jre/librt.jar/java/lang/ClassLoader.java中,是一个抽象类。
public abstract class ClassLoader {
......
class BootClassLoader extends ClassLoader {
......
}
}
BootClassLoader是ClassLoader的内部类,用于加载系统的类,如Activity。
public class BaseDexClassLoader extends ClassLoader {
private static volatile Reporter reporter = null;
private final DexPathList pathList;//实际的findclass类
BaseDexClassLoader继承ClassLoader
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);
}
}
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
PathClassLoader和DexClassLoader 都继承了 BaseDexClassLoader。有种说法是PathClassLoader只能加载已经安装过的apk中的class文件,而DexClassLoader可以用来加载未安装的apk的class文件。
这种说法实际是不正确的。在8.0(API26)之前,两者的区别在于DexClassLoader 的第二个参数optimizedDirectory,可以传入优化的dex文件,即odex的路径。而如源码中所示,8.0以后,两者都传入null,就没有什么区别了。
DexClassLoader classLoader = new DexClassLoader( apkPath, mOptFile.getAbsolutePath(), null, mContext.getClassLoader());
classLoader .loadClass( "com.cwh.plugin.Test" );
我们通过创建一个DexClassLoader对象,调用loadClass方法来加载一个类,来了解类的加载流程。
双亲委托
loadClass方法定义在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
{
//查找这个类是否已经加载了,加载则直接返回
Class<?> c = findLoadedClass(name);
//还没有加载
if (c == null) {
try {
//父类如果不为null,委托给父类
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) {
c = findClass(name);
}
}
return c;
}
//通过虚拟机进行查找
protected final Class<?> findLoadedClass(String name) {
ClassLoader loader;
if (this == BootClassLoader.getInstance())
loader = null;
else
loader = this;
//通过本地方法findLoadedClass查找
return VMClassLoader.findLoadedClass(loader, name);
}
private Class<?> findBootstrapClassOrNull(String name){
return null;
}
//空方法,由子类实现
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
如源码所示,加载过程有3层判断:
- 先通过本地方法findLoadedClass查找是否已加载过此类,如果找到,则直接将类返回。
- 如果还未加载,会通过parent的loadClass来加载。
- 如果parent没有加载到,则自己再进行加载。
第一层判断是,通过本地方法调用虚拟机来查找类是否已加载,不做分析。
第二层判断就是双亲委托,即先交给parent来加载。这个parent是DexClassLoader的最后一个参数,mContext.getClassLoader()。
Log.i( TAG, "加载器:" + mContext.getClassLoader());
加载器: dalvik.system.PathClassLoader
mContext的PathClassLoader由系统生成,方法为createSystemClassLoader()。
public abstract class ClassLoader {
private static ClassLoader createSystemClassLoader() {
String classPath = System.getProperty("java.class.path", ".");
String librarySearchPath = System.getProperty("java.library.path", "");
//parent为BootClassLoader
return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
由打印日志和PathClassLoader的创建知道,DexClassLoader 的parent是PathClassLoader,而PathClassLoader的parent是BootClassLoader ,因此,双亲委托,最终委托到了BootClassLoader 类。下面看BootClassLoader 的loadClass方法
public abstract class ClassLoader {
......
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}
return instance;
}
public BootClassLoader() {
super(null);
}
//parent类加载方法
@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
//又查了一遍是否已加载
Class<?> clazz = findLoadedClass(className);
//如果没有再通过findClass加载
if (clazz == null) {
clazz = findClass(className);
}
return clazz;
}
//调用Class的本地静态方法classForName加载
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return Class.classForName(name, false, null);
}
}
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
//类加载方法
@FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;
}
我们看到,parent又进行了一次查询类是否已加载,如果没有,则调用Class的本地方法classForName,来加载我们传入的类。
如果parent也没没有成功加载到我们的类,将进入第三层判断,调用ClassLoader 的findClass来加载。而ClassLoader 的findClass是一个空实现,由上面分析知道,BaseDexClassLoader 继承了ClassLoader,因此,我们来看BaseDexClassLoader 的findClass方法。
public class BaseDexClassLoader extends ClassLoader {
//实际的findclass类
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
}
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();
}
}
//子类自己的类加载方法
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
//实际由DexPathList 来加载
Class c = pathList.findClass(name, suppressedExceptions);//实际调用DexPathList的findClass
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 来进行加载的。我们来分析DexPathList 类。
final class DexPathList {
private static final String DEX_SUFFIX = ".dex";//文件后缀为.dex
private static final String zipSeparator = "!/";
//类加载器
private final ClassLoader definingContext;
//Element数组
private Element[] dexElements;
//类加载
public Class<?> findClass(String name, List<Throwable> suppressed) {
//遍历Element数组
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 {
private final File path;//dex 的路径
private final DexFile dexFile;//dex文件对象
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
//由DexFile的loadClassBinaryName进行查找
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
}
DexPathList 遍历成员变量Element 数组,Element 是DexPathList 的静态内部类,一个.dex文件封装成一个DexFile对象,一个DexFile对象封装成一个Element 对象。
二进制文件的读取最终通过DexFile的loadClassBinaryName来读取。
public final class DexFile {
//通过defineClass加载.dex二进制文件
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
//加载.dex二进制文件
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {
//由本地方法defineClassNative进行加载
result = defineClassNative(name, loader, cookie, dexFile);
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
//本地方法defineClassNative加载.dex二进制文件
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
DexFile dexFile)
一个apk可能会有多个.dex文件,以防止65535问题,因此,DexPathList 定义了一个Element数组来保存。
网友评论