美文网首页
ClassLoader源码分析

ClassLoader源码分析

作者: 北疆小兵 | 来源:发表于2019-11-20 21:25 被阅读0次

    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()加载字节码文件

    相关文章

      网友评论

          本文标题:ClassLoader源码分析

          本文链接:https://www.haomeiwen.com/subject/modaictx.html