ClassLoader类加载源码解析
作者:droidDing
这篇文章主要为大家详细解析了ClassLoader类加载源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
Java类加载器
1、BootClassLoader: 用于加载Android Framework层class文件。
2、PathClassLoader: 用于Android应用程序类加载器。可以加载指定的dex,jar、zip、zpk中的classes.dex
3、DexClassLoader:加载指定的dex,以及jar、zip、apk中的classes.dex
源码解析
1.ClassLoader中提供loadClass用于加载指定类
//ClassLoader.java 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 { //双亲委托机制,先让爸爸去找 if (parent != null) { c = parent.loadClass(name, false); } else { //如果parent为null,则用BootClassLoader进行加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { //如果都找不到就自己去找,此方法在子类BaseDexClassLoader类中有重写 c = findClass(name); } } return c; }
2.BaseDexClassLoader类中对findClass有重写,也是实际会使用执行的
//BaseDexClassLoader.java //查找class @Override protected Class<?> findClass(String name) throws ClassNotFoundException { ... //这里通过pathList变量来查找,而pathList是在BaseDexClassLoader的构造方法中初始化的 Class c = pathList.findClass(name, suppressedExceptions); ... return c; } private final DexPathList pathList; public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent, boolean isTrusted) { super(parent); //构造方法中初始化pathList变量 this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted); }
3.BaseDexClassLoader中是通过调用DexPathList中的findClass来实现的,那么接下来我们分析一下DexPathList是怎么实现的
//DexPathList.java //是一个Element数组,一个element中包含一个 DexFile,DexFile就代表一个Dex文件,里面的native(C/C++)函数来进行Dex的加载工作 private Element[] dexElements; public Class<?> findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { //此处调用Element的findClass来实现, Class<?> clazz = element.findClass(name, definingContext, suppressed); if (clazz != null) { return clazz; } } return null; } // Element为DexPathList的内部类 static class Element { private final File path; //一个DexFile就代表一个Dex文件 private final DexFile dexFile; //有多个构造方法,但都仅是将值传过来,让Element来持有一个DexFile public Element(DexFile dexFile) { this.dexFile = dexFile; this.path = null; } public Class<?> findClass(String name, ClassLoader definingContext, List<Throwable> suppressed) { //通过DexFile来加载类 return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null; } } DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory, boolean isTrusted) { //通过makeDexElements方法为dexElements初始化 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext, isTrusted); } //腾讯系的热修复,诸如微信tinker、qq空间qfix原理便是反射此方法,将修复后的类打包成dex,通过反射该方法来将文件转化为Element,并将新生成的element放到dexElements前面,这样下次系统再去寻找某个class时,会先从修复后的dex中来找class,找到后便不再继续查找,从而修复该class,此方式便为插桩 private static Element[] makeDexElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) { Element[] elements = new Element[files.size()]; ... for (File file : files) { if (name.endsWith(DEX_SUFFIX)) { //以 .dex 结尾的 // Raw dex file (not inside a zip/jar). //加载dex文件 dex = loadDexFile(file, optimizedDirectory, loader, elements); if (dex != null) { elements[elementsPos++] = new Element(dex, null); } } } ... return elements; } private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements) throws IOException { if (optimizedDirectory == null) { return new DexFile(file, loader, elements); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements); } }
4.这里通过 new DexFile 或者 loadDex方法来创建DexFile,两者类似,那我们拿new DexFile 来举例分析
//DexFile.java private DexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { ... //此处调用openDexFile来实现 mCookie = openDexFile(sourceName, outputName, flags, loader, elements); ... } private static Object openDexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { //此处通过调用 openDexFileNative来实现 return openDexFileNative(new File(sourceName).getAbsolutePath(), (outputName == null) ? null : new File(outputName).getAbsolutePath(), flags, loader, elements); } //openDexFileNative是一个native方法,是由C/C++来实现的 private static native Object openDexFileNative(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements);
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。