dex通过DexClassLoader和PathClassLoader,其中PathClassLoader是应用使用的ClassLoader,DexClassLoader是用与加载指定的dex文件,
两个ClassLoader代码都比较简单直接执行父类(BaseDexClassLoader)方法
PathClassLoader.java
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
DexClassLoader.java
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
BaseDexClassLoader.java
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.originalPath = dexPath;
this.pathList =
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = pathList.findClass(name);
....
return clazz;
}
@Override
protected URL findResource(String name) {
return pathList.findResource(name);
}
@Override
protected Enumeration<URL> findResources(String name) {
return pathList.findResources(name);
}
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
@Override
protected synchronized Package getPackage(String name) {
if (name != null && !name.isEmpty()) {
Package pack = super.getPackage(name);
if (pack == null) {
pack = definePackage(name, "Unknown", "0.0", "Unknown",
"Unknown", "0.0", "Unknown", null);
}
return pack;
}
return null;
}
1,构造方法创建了,DexPathList类。
2,findClass,findResource,findLibrary,getPackage 直接调用DexPathList中对应的方法
3,findClass:根据名称查找Class, findResource查找资源,findLibrary查找native library路径,getPackage 定义类名
下面查看DexPathList构造方法
public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
···
this.definingContext = definingContext;
this.dexElements =
makeDexElements(splitDexPath(dexPath), optimizedDirectory);
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}
前面都是安全校验,关键在于DexPathList维护了dexElements和nativeLibraryDirectories
其中dexElements存储了dex的文件路径或zip或dex,下面是Element的属性
class Element {
public final File file;
public final ZipFile zipFile;
public final DexFile dexFile;
}
nativeLibraryDirectories则是naive库存储的路径
下面关注makeDexElements和splitLibraryPath方法
dexElements 生成
private static ArrayList<File> splitDexPath(String path) {
return splitPaths(path, null, false);
}
private static ArrayList<File> splitPaths(String path1, String path2,
boolean wantDirectories) {
ArrayList<File> result = new ArrayList<File>();
splitAndAdd(path1, wantDirectories, result);
splitAndAdd(path2, wantDirectories, result);
return result;
}
private static void splitAndAdd(String path, boolean wantDirectories,
ArrayList<File> resultList) {
if (path == null) {
return;
}
String[] strings = path.split(Pattern.quote(File.pathSeparator));
for (String s : strings) {
File file = new File(s);
if (! (file.exists() && file.canRead())) {
continue;
}
/*
* Note: There are other entities in filesystems than
* regular files and directories.
*/
if (wantDirectories) {
if (!file.isDirectory()) {
continue;
}
} else {
if (!file.isFile()) {
continue;
}
}
resultList.add(file);
}
}
将path以“:”分开,返回文件列表
private static Element[] makeDexElements(ArrayList<File> files,
File optimizedDirectory) {
···
for (File file : files) {
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ex) {
System.logE("Unable to load dex file: " + file, ex);
}
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
try {
zip = new ZipFile(file);
} catch (IOException ex) {
/*
* Note: ZipException (a subclass of IOException)
* might get thrown by the ZipFile constructor
* (e.g. if the file isn't actually a zip/jar
* file).
*/
System.logE("Unable to open zip file: " + file, ex);
}
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ignored) {
/*
* 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). Safe to just ignore
* the exception here, and let dex == null.
*/
}
} else {
System.logW("Unknown file type for: " + file);
}
}
···
return elements.toArray(new Element[elements.size()]);
}
1,循环PathListFile,根据文件后缀执行ZipFile或者DexFile的构造方法,最终返回DexElement。
关注loadDexFile
private static DexFile loadDexFile(File file, File optimizedDirectory)
throws IOException {
if (optimizedDirectory == null) {
//PathClassLoader执行
return new DexFile(file);
} else {
//DexClassLoader执行
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
}
static public DexFile loadDex(String sourcePathName, String outputPathName,
int flags) throws IOException {
return new DexFile(sourcePathName, outputPathName, flags);
}
public DexFile(String fileName) throws IOException {
mCookie = openDexFile(fileName, null, 0);
mFileName = fileName;
guard.open("close");
//System.out.println("DEX FILE cookie is " + mCookie);
}
private DexFile(String sourceName, String outputName, int flags) throws IOException {
mCookie = openDexFile(sourceName, outputName, flags);
mFileName = sourceName;
guard.open("close");
}
两个ClassLoader都调用了DexFile的方法openDexFile方法。
vm/native/dalvik_system_DexFile.cpp
static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
JValue* pResult){
···
if (hasDexExtension(sourceName)
&& dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
ALOGV("Opening DEX file '%s' (DEX)", sourceName);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
pDexOrJar->pDexMemory = NULL;
} else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
ALOGV("Opening DEX file '%s' (Jar)", sourceName);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = false;
pDexOrJar->pJarFile = pJarFile;
pDexOrJar->pDexMemory = NULL;
}
}
···
native层是主要加载dex的逻辑并带有优化操作,当传入outputName时优化后的代码会放到这里。加载dex也会优先使用优化后的代码。/data/dalvik-cache或者传入的路径
native步骤
nativeLibraryDirectories 生成
ArrayList<File> result = splitPaths(
path, System.getProperty("java.library.path", "."), true);
return result.toArray(new File[result.size()]);
native很简单只是传入的nativepath和环境变量组成的list。如果是PathClassLoader的时候就只有环境变量
网友评论