Android类加载器源码分析
16lz
2021-12-04
基础概念
-
Android中的ClassLoader类型可分为系统ClassLoader和自定义ClassLoader。其中系统ClassLoader包括3种分别是:
-
BootClassLoader
,Android系统启动时会使用BootClassLoader
来预加载常用类,与Java中的Bootstrap ClassLoader
不同的是,它并不是由C/C++代码实现,而是由Java实现的。BootClassLoader
是ClassLoader
的一个内部类。-
PathClassLoader
,只能加载系统中已经安装过的apk。 -
DexClassLoader
,是一个可以从包含classes.dex
实体的.jar或.apk文件中加载classes的类加载器。可以用于实现dex的动态加载、代码热更新等等。可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。 -
PathClassLoader
和DexClasLoader
都是继承自BaseDexClassLoader
,它们的类加载逻辑全部写在BaseDexClassLoader
中。
-
-
源码分析
DexClassLoader分析
DexClassLoader.java
位置:~/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); }}
参数:
- dexpath:jar或apk文件目录
- optimizedDirectory:优化dex缓存目录
- libraryPath:包含native lib的目录路径
- parent:父类加载器
BaseDexClassLoader.java
位置:~/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。
ClassLoader.java
位置:~/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。
DexPathList.java
位置:~/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 suppressedExceptions = new ArrayList(); // 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 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
方法
private static Element[] makePathElements(List files, File optimizedDirectory, List suppressedExceptions) { List 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。
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的具体实现就是:
位置:~/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> dex_files; std::vector 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; }}
更多相关文章
- 在Android中如何让gif动起来
- 【ncnn android】算法移植(九)——DBface android移植
- Android的IPC机制Binder的详解汇总
- android 静音与振动
- Cordova 3.x 基础(13) -- 为Android(安卓)APK签名
- Android文件系统的结构及目录用途、操作方法 整理
- 关于android的各种disk images(看过的讲android image比较细致的
- Android(安卓)JNI详述(二)
- Android(安卓)SDK等下载说明