ClassLoader分类
Android中包含以下几种ClassLoader
- BootClassLoader:用来加载Framework层的字节码文件
- PathClassaLoader:加载已经按照到系统中的apk的class文件
- DexClassLoader:加载指定目录中的字节码文件(包括aar,sdk,jar,sdcard等路径)
- BaseDexClassLoader:是PathClassaLoader和 DexClassLoader的父类
ClassLoader加载流程
ClassLoader加载class类采用的是双亲代理模型,在加载字节码的时候,首先会查询当前classLoader是否加载过,如果已经加载过,则直接返回;如果没有加载过,则会查询parent classLoader是否有加载过,如果加载过,则返回parent加载过的字节码文件;如果parent classLoader也没加载过,才会由当前的classLoader加载;这个机制保证了同一个class类只会加载一次,加载的效率会非常高。
源码讲解
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
//步骤一
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
//步骤二
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//步骤三
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
上面步骤三findClass(name)方法在ClassLoader中是空实现,真正的实现是在子类中实现的。
先看DexClassLoader
/**
* 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 {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
DexClassLoader继承自BaseDexClassLoader,构造方法参数dexpath指定我们要加载的 dex 文件路径,optimizedDirectory指定该 dex 文件要被拷贝到哪个路径中,一般是应用程序内部路径。
由类描述可以看出DexClassLoader可以执行未安装到系统应用的类,所以DexClassLoader可以用来做动态加载。
再看PathClassLoader
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
可以看出PathClassLoader没有optimizedDirectory参数,所以只能加载安装到系统的类
PathClassLoder和DexClassLoader都没有对findClass做具体实现,其具体实现都是在BaseDexClassLoader中完成的
```
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, optimizedDirectory);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
//调用DexPathList的findClass
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
}
```
可以看到最终调用的是DexPathList的findClass方法
final class DexPathList{
private static final String DEX_SUFFIX = ".dex";
private final ClassLoader definingContext;
private final Element[] dexElements;
...
public DexPathList(ClassLoader definingContext,
String dexPath,String libraryPath,File optimizedDirectory){
...
this.dexElements = makeDexElements(splitDexPath(dexPath),optimizedDirectory,suppressedException);
...
}
public Class findClass(String name,List<Throwable> suppressed){
for (Element element : dexElements){
DexFile dex = element.dexFile;
if(dex != null){
Class clazz = dex.loadClassBinaryName(name,definingContext,suppressed);
if(clazz != null){
return clazz;
}
}
}
}
}
findClass 方法内部实现是遍历dexElements获取Element中的
dexFile对象, 而dexElements是通过makeDexElements()赋值的
private static Element[] makeElements(List<File> files,File optimizedDirectory,
List<IOException> suppressedExceptions,
boolean ignoreDexFiles,
ClassLoader loader){
Element[] elements = new Element[file.size()];
int elementsPos = 0;
for (File file : files){
File zip = null;
File dir = new File("");
DexFile dex = null;
String path = file.getPath();
String name = file.getName();
//1
if (path.contains(zipSeparator)){
...
//2
}else if(file.isDirectory()){
elements[elementsPos++] == new Element(file,true,null,null);
//3
}else if (file.isFile()){
//4
if(!ignoreDexFiles && name.endsWith(DEX_SUFFIX)){
dex = loadDexFile(file,optimizedDirectory,loader,elements);
//5
}else{
zip = file;
//6
if(!ignoreDexFiles){
dex = loadDexFile(file,optimizedDirectory,loader,elements);
}
}
}
}
}
分支4 判断如果是文件且是.dex后缀结尾,说明这个文件就是我们需要家长的dex文件 , 通过loadDexFile()来加载dex文件
private static DexFile loadDexFile(File file,File optimizedDirectory,Classloader loader,
Element[] elements) throw IOException{
if(optimizedDirectory == null){
return new DexFile(file,loader,elements);
}else{
String optimizedPath = optimizedPathFor(file,optimizedDirectory);
}
}
如果optimizedDirectory为空,说明文件就是dex文件(PathClassLoader加载系统类),如果不为空则调用optimizedPathFor获取dex文件。
根据以上分析流程可以看出,在BaseClassLoaer中调用DexPathList.findClass() ,DexPathList的构造方法中通过makeElements()从文件获取dex文件,然后转换成Elemtents对象数组),再通过Element的DexFile的findClass方法加载类,至此Class字节码文件的加载流程就结束了。
整体流程
- Classloader#findClass
- DexPathList#findClass
- DexPathList#makeDexElements
- 遍历makeDexElements,调用element.dexFile. dex.loadClassBinaryName()加载字节码文件
网友评论