ClassLoader分类
在JVM中,有三个ClassLoader,分别是:
BootsTrap ClassLoader,Extension ClassLoader,App ClassLoader.
同样的,在Android DVM(ART类似,具体实现不同)中也有三个ClassLoader:
BootClassLoader:主要用于加载系统类,包括Java和Android的类库
PathClassLoader:主要加载应用(DVM已安装)中的类,它的加载路径固定,无法指定
DexClassLoader :可以加载任意路径的Zip,Jar,Apk,可以实现动态加载(热更新,插件化)
PathClassLoader和DexClassLoader的源码
public class PathClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code PathClassLoader} that operates on a given list of files
* and directories. This method is equivalent to calling
* {@link #PathClassLoader(String, String, ClassLoader)} with a
* {@code null} value for the second argument (see description there).
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param parent the parent class loader
*/
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
/**
* Creates a {@code PathClassLoader} that operates on two given
* lists of files and directories. The entries of the first list
* should be one of the following:
*
*
* - JAR/ZIP/APK files, possibly containing a "classes.dex" file as
* well as arbitrary resources.
*
- Raw ".dex" files (not inside a zip file).
*
*
* The entries of the second list should be directories containing
* native library files.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @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 PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
public class DexClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code DexClassLoader} that finds interpreted and native
* code. Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
*
* The path lists are separated using the character specified by the
* {@code path.separator} system property, which defaults to {@code :}.
*
* @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 directory where optimized dex files
* should be written; must not be {@code null}
* @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 DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
可以看到,两者只有构造方法,均无具体实现方法。
ClassLoader加载过程
================
PathClassLoader和DexClassLoader 都继承BaseDexClassLoader
,先看BaseDexClassLoader
的构造方法:
/**
* Constructs an instance.
*
* @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 directory where optimized dex files
* should be written; may be {@code null}
* @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/*odex目录*/,
String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
findClass()
方法:
#BaseDexClassLoader.java
protected Class> findClass(String name) throws ClassNotFoundException {
List suppressedExceptions = new ArrayList();
//调用DexPathList.findClass,找到直接返回,找不到直接throw exception
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;
}
这里的pathList是DexPathList实例:
#DexPathList.java
public Class findClass(String name, List suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
//找到dex file
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
DexFile.loadClassBinaryName():
#DexFile.java
public Class loadClassBinaryName(String name, ClassLoader loader, List suppressed) {
return defineClass(name, loader, mCookie, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, long cookie,
List suppressed) {
Class result = null;
try {
result = defineClassNative(name, loader, cookie);
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
//调用本地方法defineClassNative
private static native Class defineClassNative(String name, ClassLoader loader,
Object cookie, DexFile dexFile)throws ClassNotFoundException, NoClassDefFoundError;
DexFile.defineClassNative()具体实现请看这里:
art\runtime\native\dalvik_system_DexFile.cc
可以看出,BaseDexClassLoader中有个pathList对象,pathList中包含一个DexFile的数组dexElements,由上面分析知道,dexPath传入的原始dex(.apk,.zip,.jar等)文件在optimizedDirectory文件夹中生成相应的优化后的odex文件,dexElements数组就是这些odex文件的集合,如果不分包一般这个数组只有一个Element元素,也就只有一个DexFile文件,而对于类加载呢,就是遍历这个集合,通过DexFile去寻找。最终调用native方法的defineClass。
参考
Android动态加载之ClassLoader详解
Android中的ClassLoader