Android中的ClassLoader类型可分为系统ClassLoader和自定义ClassLoader。其中系统ClassLoader包括3种分别是:java
BootClassLoader
,Android系统启动时会使用BootClassLoader
来预加载经常使用类,与Java中的Bootstrap ClassLoader
不一样的是,它并非由C/C++代码实现,而是由Java实现的。BootClassLoader
是ClassLoader
的一个内部类。android
PathClassLoader
,只能加载系统中已经安装过的apk。DexClassLoader
,是一个能够从包含classes.dex
实体的.jar或.apk文件中加载classes的类加载器。能够用于实现dex的动态加载、代码热更新等等。能够加载jar/apk/dex,能够从SD卡中加载未安装的apk。PathClassLoader
和DexClasLoader
都是继承自 BaseDexClassLoader
,它们的类加载逻辑所有写在BaseDexClassLoader
中。位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
继承BaseDexClassLoader
public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), libraryPath, parent); } }
参数:git
位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
继承ClassLoader
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); } ... }
首先调用父类的构造函数super(parent)
:ClassLoader
而后调用并初始化了DexPathList类型的对象pathList。缓存
位置:~/android6.0.1_r66/libcore/libart/src/main/java/java/lang/ClassLoader.java
public abstract class ClassLoader { ... protected ClassLoader(ClassLoader parentLoader) { this(parentLoader, false); } ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { if (parentLoader == null && !nullAllowed) { throw new NullPointerException("parentLoader == null && !nullAllowed"); } parent = parentLoader; } ... }
该构造函数把传进来的父类加载器赋给了私有变量parent。app
位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
final class DexPathList { private static final String DEX_SUFFIX = ".dex"; private static final String zipSeparator = "!/"; ... public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) { if (definingContext == null) { throw new NullPointerException("definingContext == null"); } if (dexPath == null) { throw new NullPointerException("dexPath == null"); } if (optimizedDirectory != null) { if (!optimizedDirectory.exists()) { throw new IllegalArgumentException( "optimizedDirectory doesn't exist: " + optimizedDirectory); } if (!(optimizedDirectory.canRead() && optimizedDirectory.canWrite())) { throw new IllegalArgumentException( "optimizedDirectory not readable/writable: " + optimizedDirectory); } } this.definingContext = definingContext; ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); // save dexPath for BaseDexClassLoader this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions); this.nativeLibraryDirectories = splitPaths(libraryPath, false); this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null, suppressedExceptions); if (suppressedExceptions.size() > 0) { this.dexElementsSuppressedExceptions = suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]); } else { dexElementsSuppressedExceptions = null; } } }
首先对传入参数的验证,而后调用makeDexElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions, ClassLoader loader)
方法函数
private static Element[] makePathElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions) { List<Element> elements = new ArrayList<>(); /* * Open all files and load the (direct or contained) dex files * up front. */ for (File file : files) { File zip = null; File dir = new File(""); DexFile dex = null; String path = file.getPath(); String name = file.getName(); if (path.contains(zipSeparator)) { String split[] = path.split(zipSeparator, 2); zip = new File(split[0]); dir = new File(split[1]); } else if (file.isDirectory()) { // We support directories for looking up resources and native libraries. // Looking up resources in directories is useful for running libcore tests. elements.add(new Element(file, true, null, null)); } else if (file.isFile()) { if (name.endsWith(DEX_SUFFIX)) {//原始的dex文件处理,而不是在zip或者jar中的dex文件 try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ex) { System.logE("Unable to load dex file: " + file, ex); } } else {//处理zip或者jar包中的dex文件 zip = file; try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException suppressed) { /* * IOException might get thrown "legitimately" by the DexFile constructor if * the zip file turns out to be resource-only (that is, no classes.dex file * in it). * Let dex == null and hang on to the exception to add to the tea-leaves for * when findClass returns null. */ suppressedExceptions.add(suppressed); } } } else { System.logW("ClassLoader referenced unknown path: " + file); } if ((zip != null) || (dex != null)) { elements.add(new Element(dir, false, zip, dex)); } } return elements.toArray(new Element[elements.size()]); }
dex文件、zip或者jar包中的dex文件都会调用loadDexFile
。源码分析
private static DexFile loadDexFile(File file, File optimizedDirectory) throws IOException { if (optimizedDirectory == null) { return new DexFile(file); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0); } }
若是optimizedDirectory为null就会新建一个DexFile对象,调用DexFile的构造函数。优化
位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexFile.java
... public DexFile(File file) throws IOException { this(file.getPath()); } public DexFile(String fileName) throws IOException {//fileName就是上一个构造函数的file.getPath() mCookie = openDexFile(fileName, null, 0); mFileName = fileName; guard.open("close"); } ...
关键函数是openDexFile。this
private static Object openDexFile(String sourceName, String outputName, int flags) throws IOException { // Use absolute paths to enable the use of relative paths when testing on host. return openDexFileNative(new File(sourceName).getAbsolutePath(), (outputName == null) ? null : new File(outputName).getAbsolutePath(), flags); }
openDexFileNative是一个native函数,须要找到具体实现:grep -rns
:-r指定要查找的是目录 -n显示行号 -s不显示错误信息
因此openDexFileNative的具体实现就是:spa
位置:~/android6.0.1_r66/art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative( JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) { ScopedUtfChars sourceName(env, javaSourceName); if (sourceName.c_str() == nullptr) { return 0; } NullableScopedUtfChars outputName(env, javaOutputName); if (env->ExceptionCheck()) { return 0; } ClassLinker* linker = Runtime::Current()->GetClassLinker(); std::vector<std::unique_ptr<const DexFile>> dex_files; std::vector<std::string> error_msgs; dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs); if (!dex_files.empty()) { jlongArray array = ConvertNativeToJavaArray(env, dex_files); if (array == nullptr) { ScopedObjectAccess soa(env); for (auto& dex_file : dex_files) { if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { dex_file.release(); } } } return array; } else { ScopedObjectAccess soa(env); CHECK(!error_msgs.empty()); // The most important message is at the end. So set up nesting by going forward, which will // wrap the existing exception as a cause for the following one. auto it = error_msgs.begin(); auto itEnd = error_msgs.end(); for ( ; it != itEnd; ++it) { ThrowWrappedIOException("%s", it->c_str()); } return nullptr; } }