- BaseDexClassLoader
- DexPathList
- DexFile
- DexClassLoader
- PathClassLoader
- 参考
对于 Android 而言,最终的 apk 文件包含的是 dex 类型的文件,dex 文件是将 class 文件重新打包,打包的规则又不是简单地压缩,而是完全对 class 文件内部的各种函数表,变量表进行优化,产生一个新的文件,即 dex 文件。而加载这种特殊的 Class 文件就需要特殊的类加载器 DexClassLoader。
源码位于 AOSP/libcore/dalvik/src/main/java/dalvik/system
目录下。
BaseDexClassLoader
/**
* Base class for common functionality between various dex-based
* {@link ClassLoader} implementations.
*/
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
...
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
// First, check whether the class is present in our shared libraries.
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
return loader.loadClass(name);
}
}
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
List suppressedExceptions = new ArrayList();
Class c = pathList.findClass(name, suppressedExceptions);
return c;
}
public void addDexPath(String dexPath) {
addDexPath(dexPath, false /*isTrusted*/);
}
public void addDexPath(String dexPath, boolean isTrusted) {
pathList.addDexPath(dexPath, null /*optimizedDirectory*/, isTrusted);
}
@Override
protected URL findResource(String name) {
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
URL url = loader.getResource(name);
if (url != null) {
return url;
}
}
}
return pathList.findResource(name);
}
}
- 在构造函数中初始化
DexPathList
; - 通过 DexPathList 查找 Class 和 资源;
DexPathList
/**
* A pair of lists of entries, associated with a {@code ClassLoader}.
* One of the lists is a dex/resource path — typically referred
* to as a "class path" — list, and the other names directories
* containing native code libraries. Class path entries may be any of:
* a {@code .jar} or {@code .zip} file containing an optional
* top-level {@code classes.dex} file as well as arbitrary resources,
* or a plain {@code .dex} file (with no possibility of associated
* resources).
*
* This class also contains methods to use these lists to look up
* classes and resources.
*/
/*package*/ final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
private Element[] dexElements;
public DexPathList(ClassLoader definingContext, String dexPath,
String librarySearchPath, File optimizedDirectory) {
...
// save dexPath for BaseDexClassLoader
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext);
...
}
/**
* Makes an array of dex/resource path elements, one per element of
* the given array.
*/
private static Element[] makeDexElements(List files, File optimizedDirectory,
List suppressedExceptions, ClassLoader loader) {
Element[] elements = new Element[files.size()];
int elementsPos = 0;
/*
* Open all files and load the (direct or contained) dex files up front.
*/
for (File file : files) {
if (file.isDirectory()) {
// We support directories for looking up resources. Looking up resources in
// directories is useful for running libcore tests.
elements[elementsPos++] = new Element(file);
} else if (file.isFile()) {
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
if (dex != null) {
elements[elementsPos++] = new Element(dex, null);
}
} else {
DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
if (dex == null) {
elements[elementsPos++] = new Element(file);
} else {
elements[elementsPos++] = new Element(dex, file);
}
}
}
}
if (elementsPos != elements.length) {
elements = Arrays.copyOf(elements, elementsPos);
}
return elements;
}
/**
* Constructs a {@code DexFile} instance, as appropriate depending on whether
* {@code optimizedDirectory} is {@code null}. An application image file may be associated with
* the {@code loader} if it is not null.
*/
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file, loader, elements);
} else {
// 间接调用 new DexFile()
return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
}
}
static class Element {
/**
* Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath
* should be null), or a jar (in which case dexZipPath should denote the zip file).
*/
@UnsupportedAppUsage
public Element(DexFile dexFile, File dexZipPath) {
this.dexFile = dexFile;
this.path = dexZipPath;
}
public Class> findClass(String name, ClassLoader definingContext,
List suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null;
}
public URL findResource(String name) {
// We support directories so we can run tests and/or legacy code
// that uses Class.getResource.
if (path != null && path.isDirectory()) {
File resourceFile = new File(path, name);
if (resourceFile.exists()) {
return resourceFile.toURI().toURL();
}
}
return null;
}
}
}
DexPathList 通过 makeDexElements()
方法返回 Element[]
数组,Element 中保存有 DexFile
的对象。
DexFile
/**
* Loads DEX files. This class is meant for internal use and should not be used by applications.
*/
@Deprecated
public final class DexFile {
DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
mCookie = openDexFile(fileName, null, 0, loader, elements);
}
private static Object openDexFile(String sourceName, String outputName, int flags, ...) throws IOException {
return openDexFileNative(...);
}
/*
* Open a DEX file. The value returned is a magic VM cookie. On
* failure, an IOException is thrown.
*/
private static native Object openDexFileNative(String sourceName, String outputName, int flags, ...);
static DexFile loadDex(String sourcePathName, String outputPathName,
int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
}
public Class loadClass(String name, ClassLoader loader) {
String slashName = name.replace('.', '/');
return loadClassBinaryName(slashName, loader, null);
}
public Class loadClassBinaryName(String name, ClassLoader loader, List suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List suppressed) {
Class result = defineClassNative(name, loader, cookie, dexFile);
return result;
}
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)
}
- 在构造方法中处理 Dex 文件;
- 通过
native 方法
打开 Dex 文件和定义 Class。
JNI 实现文件为 AOSP/art/runtime/native/
文件夹下。
// dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, ...) {
Runtime* const runtime = Runtime::Current();
std::vector> dex_files;
const OatFile* oat_file = nullptr;
dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), class_loader, dex_elements, ...);
if (!dex_files.empty()) {
jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
return array;
} else {
return nullptr;
}
}
关键是 OpenDexFilesFromOat() 方法,转到 oat_file_manager.cc 文件。
// oat_file_manager.cc
std::vector> OatFileManager::OpenDexFilesFromOat( const char* dex_location,
jobject class_loader, jobjectArray dex_elements, ...) {
Runtime* const runtime = Runtime::Current();
OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, !runtime->IsAotCompiler());
// Get the oat file on disk.
std::unique_ptr oat_file(oat_file_assistant.GetBestOatFile().release());
if (oat_file != nullptr) {
// Take the file only if it has no collisions, or we must take it because of preopting.
bool accept_oat_file =
!HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
if (accept_oat_file) {
source_oat_file = RegisterOatFile(std::move(oat_file));
*out_oat_file = source_oat_file;
}
}
std::vector> dex_files;
// Load the dex files from the oat file.
if (source_oat_file != nullptr) {
bool added_image_space = false;
if (source_oat_file->IsExecutable()) {
std::unique_ptr image_space =
kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
runtime->GetHeap()->AddSpace(image_space.get());
added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), ...);
}
if (!added_image_space) {
dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
}
}
return dex_files;
}
主要是将 dex 文件转 oat。如果转 oat 失败,那就打开 dex 的方式来加载。
DexClassLoader
能够加载未安装的 jar、apk 或 dex。
/**
* A class loader that loads classes from {@code .jar} and {@code .apk} files containing a {@code classes.dex} entry. This can be used to execute code not installed as part of an application.
*/
public class DexClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code DexClassLoader} that finds interpreted and native code. Interpreted classes are found in a set of DEX files contained in Jar or APK files.
*/
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
DexClassLoader 继承 BaseDexClassLoader,只有一个构造函数,optimizedDirectory 传 null。
PathClassLoader
只能从本地文件系统加载,不能从网络加载类。
/**
* Provides a simple {@link ClassLoader} implementation that operates on a list of files and directories in the local file system, but does not attempt to load classes from the network. Android uses this class for its system class loader and for its application class loader(s).
*/
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);
}
}
参考
[1] DexClassLoader - developer
[2] PathClassLoader - developer
[3] Android 类加载器 ClassLoader - Gityuan 博客
[4] Android 11 在线源码