dex文件加载系列文章链接:
PathClassLoader、DexClassLoader
dex文件加载流程(一)
dex文件加载流程(二)
dex文件加载流程(三)
InMemoryDexClassLoader
InMemoryDexClassLoader加载内存dex的流程
一、PathClassLoader和DexClassLoader
Android启动APP时,会加载dex文件,而与加载dex文件相关的是两个ClassLoader:
PathClassLoader
只能加载系统中已经安装过的apk
DexClassLoader
可以加载jar、apk、dex,也可以从SD卡中加载未安装的apk
PathClassLoader和DexClassLoader对应代码分别如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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); } }
public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), librarySearchPath, parent); } }
|
两者的父类都是BaseDexClassLoader,且都是用父类的四个参数的构造方法,对应代码如下:
1 2 3 4 5 6 7 8 9 10
| public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
if (reporter != null) { reporter.report(this.pathList.getDexPaths()); } }
|
在构造方法中,创建一个DexPathList实例。
二、DexPathList()
DexPathList代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory) { ...... this.definingContext = definingContext;
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext);
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
if (suppressedExceptions.size() > 0) { this.dexElementsSuppressedExceptions = suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]); } else { dexElementsSuppressedExceptions = null; } }
|
构造方法中,主要是调用了makeDexElements()
方法(makePathElements()
先不讲),赋值给this.dexElements
,该类型为Element数组,查看Element类的代码可知,类中有DexFile类型的dexFile字段。
三、makeDexElements()
回到makeDexElements()
方法,该方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| private static Element[] makeDexElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions, ClassLoader loader) { Element[] elements = new Element[files.size()]; int elementsPos = 0; for (File file : files) { if (file.isDirectory()) { elements[elementsPos++] = new Element(file); } else if (file.isFile()) { String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) { try { DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements); if (dex != null) { elements[elementsPos++] = new Element(dex, null); } } catch (IOException suppressed) { System.logE("Unable to load dex file: " + file, suppressed); suppressedExceptions.add(suppressed); } } else { DexFile dex = null; try { dex = loadDexFile(file, optimizedDirectory, loader, elements); } catch (IOException suppressed) { suppressedExceptions.add(suppressed); }
if (dex == null) { elements[elementsPos++] = new Element(file); } else { elements[elementsPos++] = new Element(dex, file); } } } else { System.logW("ClassLoader referenced unknown path: " + file); } } if (elementsPos != elements.length) { elements = Arrays.copyOf(elements, elementsPos); } return elements; }
|
遍历所有文件,寻找dex文件并调用loadDexFile()
加载dex文件。
四、loadDexFile()
该方法如下:
1 2 3 4 5 6 7 8 9 10
| 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); } }
|
这里再次讲解一下dex文件优化。在Android系统中,应用程序首次启动,会对应用的dex文件进行优化,并在cache/dalvik-cache
目录下生成缓存文件,以加快应用的启动速度。代码中optimizedDirectory
即指缓存文件所在路径,默认cache/dalvik-cache
目录。
回到代码中,如果APP没有相应的缓存文件,则通过调用DexFile()
来解析dex文件,否则调用DexFile.loadDex()
方法解析缓存文件。
4.1 DexFile()
对应代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| DexFile(File file, ClassLoader loader, DexPathList.Element[] elements) throws IOException { this(file.getPath(), loader, elements); }
DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException { mCookie = openDexFile(fileName, null, 0, loader, elements); mInternalCookie = mCookie; mFileName = fileName; }
|
最终调用openDexFile()
方法,现在这里停下。
4.2 DexFile.loadDex()
对应代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static DexFile loadDex(String sourcePathName, String outputPathName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { return new DexFile(sourcePathName, outputPathName, flags, loader, elements); }
private DexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { ...... mCookie = openDexFile(sourceName, outputName, flags, loader, elements); mInternalCookie = mCookie; mFileName = sourceName; }
|
可以看到,不管调用的是new DexFile()
还是DexFile.loadDex()
,最终都会调用openDexFile()
。
五、openDexFile()
对应代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| private static Object openDexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { return openDexFileNative(new File(sourceName).getAbsolutePath(), (outputName == null) ? null : new File(outputName).getAbsolutePath(), flags, loader, elements); }
|
最终调用openDexFileNative()
方法,顾名思义,这是个native方法,在目录art/runtime/native/dalvik_system_DexFile.cc
下。
在下一篇文章中,将继续沿着openDexFileNative()
方法分析dex文件加载流程。
六、总览图
参考:
入门ART虚拟机(1)——加载DEX文件 - 简书 (jianshu.com)
Dex文件加载以及类加载流程_两个dex间能引用类吗-CSDN博客
http://aospxref.com/android-8.0.0_r36/