Android 的 Classloader
通过dex字节码来加载,合并多个class文件为一个classe.dex文件。
Android共有三种类加载器:
-
BootClassLoader:父类构造器
-
PathClassLoader:一般是加载指定路径/data/app中的apk,也就是安装到手机中的apk。所以一般作为默认的加载器。
-
DexClassLoader:从包含classes.dex的jar或者apk中,加载类的加载器,可用于动态加载。
看PathClassLoader
和DexClassLoader
源码,都是继承自BaseDexClassLoader
。
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent); //见下文
}
}
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent); //见下文
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
我们可以看到PathClassLoader
的两个参数都为null,表明只能接受固定的dex文件,而这个文件是只能在安装后出现的。而DexClassLoader
中optimizedDirectory
,和librarySearchPath
都是可以自己定义的,说明我们可以传入一个jar
或者apk
包,保证解压缩后是一个dex
文件就可以操作了。因此,我们通常使用DexClassLoader
来进行插件化和热修复。
可以看到,BaseDexClassLoader
有一个相当重要的过程就是初始化DexPathList
。初始化DexPathList
的过程主要是收集dexElements
和nativeLibraryPathElements
。一个Classloader
可以包含多个dex
文件,每个dex
文件被封装到一个Element
对象。这element
对象在初始化和热修复逻辑中是相当重要的。当查找某个类时,会遍历dexElements
,如果找到就返回,否则继续遍历。所以当多个dex中有相同的类,只会加载前面的dex
中的类。下面是这段逻辑的具体实现
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
//找到目标类,则直接返回
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
return null;
}