-
JVM加载的是class文件,系统提供的类加载器(BootStrapClassLoader、ExtensionClassLoader、ApplicationClassLoader)
-
安卓虚拟机加载的是dex文件,系统提供的dex加载器(DexClassLoader、PathClassLoader)
class文件、资源文件、AndroidManifest.xml 经过编译打包,成为apk安装包;apk后缀修改为.zip,解压后可以看到dex文件。
1、classLoader代码测试
在Activity的onCreate()中添加如下代码,得到当前classLoader,并递归父classLoader
ClassLoader classLoader = getClassLoader();
if (classLoader != null){
while (classLoader.getParent()!=null){
classLoader = classLoader.getParent();
}
}
当前classLoader(PathClassLoader):dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.example.dexloader-rEcmqp5bzgybFOqTwgSCzg==/base.apk”],nativeLibraryDirectories=[/data/app/com.example.dexloader-rEcmqp5bzgybFOqTwgSCzg==/lib/x86, /system/lib, /system/vendor/lib]]]
父classLoader(BootClassLoader):java.lang.BootClassLoader@c25f209
2、Android Dex ClassLoader源码解析
源码地址:
https://www.androidos.net.cn/sourcecode
相关类:
- ClassLoader抽象类,持有parent,提供loadClass()方法
- BootClassLoader:Android系统启动时创建,用于加载一些系统Framework层级需要的类,APP启动的时候也会把这个BootClassLoader实例传进来。
- BaseDexClassLoader :DexClassLoader的基类
- PathClassLoader:
- DexClassLoader:
- DexPathList:持有dexElements,nativeLibraryElements,静态内部类Element、静态内部类NativeLibraryElement
- DexFile:
2.1、ClassLoader抽象类加载器
抽象类
- 判断当前类加载器是否已经加载过指定类,若已加载则直接返回,否则继续执行
- 调用parent的类加载递归加载该类,检测是否加载,若已加载则直接返回,否则继续执行
- 调用当前类加载器,通过findClass加载
public abstract class ClassLoader {
private final ClassLoader parent;
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name); //步骤一,判断当前classLoader是否已经加载过该类
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false); //步骤二,递归父classloader的loadClass()
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
c = findClass(name); //步骤三,以上均没找到,使用当前classloader去加载
}
}
return c;
}
private Class<?> findBootstrapClassOrNull(String name) { return null; }
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
2.2、BaseDexClassLoader
- 成员变量pathList,是DexPathList对象
- findClass、findResources、findLibrary都是通过pathList实现的
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
/**
* dexPath 需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 “:”
* optimizedDirectory 优化后的dex文件存放目录,这里其实没用到,不知道是不是因为后来放弃了
* libraryPath 目标类中使用的C/C++库so文件的路径,每个目录用File.pathSeparator间隔开; 可以为null
* parent 该类装载器的父装载器,一般用当前执行类的装载器
*/
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new dalvik.system.DexPathList(this, dexPath, librarySearchPath, null);
if (reporter != null) {
reporter.report(this.pathList.getDexPaths());
}
}
@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;
}
@Override
protected URL findResource(String name) {
return pathList.findResource(name);
}
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
}
2.3、DexClassLoader
- 只是简单的继承BaseDexClassLoader
- 提供一个构造函数 dexPath、librarySearchPath、parent
- optimizedDirectory父类没有用到
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
2.3、PathClassLoader
- 只是简单的继承BaseDexClassLoader
- 提供两个构造函数,一个可以指定librarySearchPath,一个不指定librarySearchPath
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);
}
}
2.4、DexPathList
- findClass(String name, List suppressed),遍历dexElements,返回Class<?>对象
- findResource(String name),遍历dexElements,返回URL对象
- findLibrary(String libraryName),遍历nativeLibraryPathElements,返回library的全路径
final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
private final ClassLoader definingContext;
private Element[] dexElements;
private final NativeLibraryElement[] nativeLibraryPathElements;
/**
* 给dexElements、nativeLibraryPathElements赋值
*/
public DexPathList(ClassLoader definingContext, String dexPath,
String librarySearchPath, File optimizedDirectory) {
this.definingContext = definingContext;
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
suppressedExceptions, definingContext);
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true);
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
}
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader) {
return elements;
}
/**
* 构建一个DexFile实例
*/
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements)
throws IOException {
return new DexFile(file, loader, elements);
}
/**
* 遍历dexElements中的每个Element,通过Element去查找class
*/
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
return null;
}
/**
* 遍历dexElements中的每个Element,通过Element去查找Resource
*/
public URL findResource(String name) {
for (Element element : dexElements) {
URL url = element.findResource(name);
if (url != null) {
return url;
}
}
return null;
}
/**
* 遍历nativeLibraryPathElements中的每个NativeLibraryElement,通过NativeLibraryElement去查找library
*/
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);
for (NativeLibraryElement element : nativeLibraryPathElements) {
String path = element.findNativeLibrary(fileName);
if (path != null) {
return path;
}
}
return null;
}
static class Element {
private final File path;
private final DexFile dexFile;
public Element(DexFile dexFile, File dexZipPath) {
this.dexFile = dexFile;
this.path = dexZipPath;
}
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null;
}
public URL findResource(String name) {
if (path != null && path.isDirectory()) {
File resourceFile = new File(path, name);
if (resourceFile.exists()) {
try {
return resourceFile.toURI().toURL();
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
}
return null;
}
}
static class NativeLibraryElement {
//全删了
}
}
2.5 DexFile
public final class DexFile {
private Object mCookie;
private Object mInternalCookie;
private final String mFileName;
DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
mCookie = openDexFile(fileName, null, 0, loader, elements);
mInternalCookie = mCookie;
mFileName = fileName;
//System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
}
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);
} catch (NoClassDefFoundError e) {}
return result;
}
//native方法去查找
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)
}
总结:
热修复核心逻辑: 在DexPathList.findClass()过程,一个Classloader可以包含多个dex文件,每个dex文件被封装到一个Element对象,这些Element对象排列成有序的数组dexElements。当查找某个类时,会遍历所有的dex文件,如果找到则直接返回,不再继续遍历dexElements。也就是说当两个类不同的dex中出现,会优先处理排在前面的dex文件,这便是热修复的核心精髓,将需要修复的类所打包的dex文件插入到dexElements前面。