ClassLoader 的类型
- java 中的ClassLoader 加载的是jar 和class文件
- Android 中的ClassLoader 加载的是dex文件
两种ClassLoader类型
- 系统类加载器
- 自定义加载器
系统类加载器 (3种)
- BootClassLoader
- PathClassLoader
- DexClassLoader
BootClassLoader
- Android 系统启动时会使用BootClassLoader来预加载常用类,而BootClassLoader是由java实现的
- java 中的BootstrapClassLoader 是由C/C++代码实现的
BootClassLoader 代码
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;
}
......
- BootClassLoader 是 ClassLoader 的内部类
- BootClassLoader 继承自ClassLoader
- BootClassLoader是个单例类
- BootClassLoader 的访问修饰符是默认的,只有在同一个包中才可以访问,
DexClassLoader
用途
- DexClassLoader 可以加载dex文件
- DexClassLoader也可以加载包含dex的压缩文件(apk 和jar文件)
DexClassLoader源码如下
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.
*
* <p>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 libraryPath 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 libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
DexClassLoader继承自BaseDexClassLoader,方法都在BaseDexClassLoader中实现
DexClassLoader 的构造方法有如下4个参数
- dexPath:dex相关文件路径集合,多个路径用文件分隔符分隔,默认文件分隔符为":"
- optimizedDirectory:解压的dex文件存储路径,这个路径必须是一个内部存储路径:如/data/data//…
- librarySearchPath:包含C/C++库的路径集合,多个路径用文件分隔符分隔,可以为null
- parent:父加载器
PathClassLoader
用途:
- PathClassLoader 用来加载系统类和应用程序的类
PathClassLoader的代码
/**
* Provides a simple {@link ClassLoader} implementation that operates on a list
* of files and directories in the local file system, but does not attempt to
* load classes from the network. Android uses this class for its system class
* loader and for its application class loader(s).
*/
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:
*
* <ul>
* <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
* well as arbitrary resources.
* <li>Raw ".dex" files (not inside a zip file).
* </ul>
*
* 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 libraryPath 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 libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
}
- PathClassLoader 继承自BaseDexClassLoader,也都在BaseDexClassLoader中实现
- 在PathClassLoader的构造方法中没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的值为/data/dalvik-cache
- PathClassLoader无法自定义解压dex文件存储路径
- PathClassLoader通常用来加载已经安装的apk的dex文件(安装的apk的dex文件会存储在/data/dalvik-cache中)
ClassLoader 的继承关系
先看图再解释
- ClassLoader 是一个抽象类,其中定义了ClassLoader的主要功能,BootClassLoader是它的内部类
- SecureClassLoader 类和JDK8中的SecureClassLoaderL类的代码是一样的,它集成了抽象类ClassLoader,SecureClassLoader 并不是ClassLoader 的实现类,而是扩展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性
- URLClassLoader 类和JDK8 中的URLClassLoader类代码是一样的,它继承自SecureClassLoader,用来通过URL路径从jar文件和文件夹中加载类和资源
- InMemoryDexClassLoader 是Android 8.0 新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件
- BaseDexClassLoader 继承自ClassLoader ,是抽象类ClassLoader的具体实现类,PathClassLoader,DexClassLoader和InMemoryDexClassLoader 都继承自它
ClassLoader 的加载过程
- Android 的ClassLoader 同样遵循双亲委托模式
- ClassLoader的加载方法为 loadClass
- loadClass方法被定义在抽象类ClassLoader中
ClassLoader -> loadClass 方法源码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded 首先,检查类是否已经加载
Class c = findLoadedClass(name);//1
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {//2
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);//3
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//4
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);//5
// this is the defining class loader; record the stats
}
}
return c;
}
以下对上面代码的说明
- 注释1:检查传入的类是否已经加载,如果已经加载就返回该类
- 注释2:判断父加载器是否存在,存在就调用父加载器的loadClass方法,如果不存在就调用注释3处的findBootstrapClassOrNull 方法,这个方法会直接返回null
- 如果注解4处代码成立,说明向上委托流程没有检查出类已经被加载,就会执行注释5处的findClass方法来进行查找流程
下面为注释5处的findClass 方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
上面的findClass方法中直接抛出异常, 说明需要子类实现,下面为BaseDexClassLoader的代码
public class BaseDexClassLoader extends ClassLoader {
// 需要加载的dex列表
private final DexPathList pathList;
// dexPath要加载的dex文件所在的路径,optimizedDirectory是odex将dexPath
// 处dex优化后输出到的路径,这个路径必须是手机内部路劲,libraryPath是需要
// 加载的C/C++库路径,parent是父类加载器对象
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>();
// 使用pathList对象查找name类
Class c = pathList.findClass(name, suppressedExceptions);//1
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;
}
}
- 在注释 1 的地方调用了 DexPathList 的findClass方法
下面看下DexPathList 中的 findClass 方法
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {//1
Class<?> clazz = element.findClass(name, definingContext, suppressed);//2
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
- 注释1处遍历Element 数组dexElements
- 注释2处调用Element的findClass方法
- Element是 DexPathList 的静态内部类
Element 源码如下
/*package*/ 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;
/**
* Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath
* should be null), or a jar (in which case dexZipPath should denote the zip file).
*/
public Element(DexFile dexFile, File dexZipPath) {
this.dexFile = dexFile;
this.path = dexZipPath;
}
public Element(DexFile dexFile) {
this.dexFile = dexFile;
this.path = null;
}
public Element(File path) {
this.path = path;
this.dexFile = null;
}
.........
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;//1
}
- 从Element 的构造方法可以看出,其内部封装了DexFile,它用于加载dex
- 在注释1处如果DexFile不为null就调用 DexFile 的loadClassBinaryName 方法
DexFile 中 loadClassBinaryName 方法源码如下
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {
result = defineClassNative(name, loader, cookie, dexFile);//1
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
- 在loadClassBinaryName 方法中调用了defineClass方法
- 注解1处调用了defineClassNative方法来加载dex相关文件,这个方法是Native方法
到此ClassLoader 的查找流程完成,可以概括为下图