ClassLoader源码分析

2019-11-20  本文已影响0人  北疆小兵

ClassLoader分类

Android中包含以下几种ClassLoader

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字节码文件的加载流程就结束了。

整体流程

上一篇 下一篇

猜你喜欢

热点阅读