美文网首页
Android中的ClassLoader

Android中的ClassLoader

作者: xingstarx | 来源:发表于2018-03-07 14:59 被阅读20次

    Android中的ClassLoader

    1. Android中有哪几种ClassLoader?它们的作用和区别是什么?

    有PathClassLoader, DexClassLoader, BootClassLoader

    PathClassLoader: 用来加载系统类和应用程序代码(具体见PathClassLoader的构造方法的注释),只能本地文件系统上的文件和目录,而不能是网络上的

    DexClassLoader: 用来加载dex,jar,apk,可以加载未安装在应用程序上的代码(DexClassLoader需要提供一个应用私有的可写入的目录,用来存放被优化的classes)(具体可以看DexClassLoader构造方法上英文的注释文档)

    PathClassLoader, DexClassLoader 都是继承自 BaseDexClassLoader,两者都是调用的BaseDexClassLoader的方法

    /**
     * Provides a simple {@link ClassLoader} implementation that operates on a list
     * of files and directories in the local file system, but does not attempt to
     * load classes from the network. Android uses this class for its system class
     * loader and for its application class loader(s).
     */
    public PathClassLoader(String dexPath, String libraryPath,
                ClassLoader parent) {
            super(dexPath, null, libraryPath, parent);
    }
    /**
     * 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.
     *
     * <p>This class loader requires an application-private, writable directory to
     * cache optimized classes. Use {@code Context.getDir(String, int)} to create
     * such a directory: <pre>   {@code
     *   File dexOutputDir = context.getDir("dex", 0);
     * }</pre>
     *
     * <p><strong>Do not cache optimized classes on external storage.</strong>
     * External storage does not provide access controls necessary to protect your
     * application from code injection attacks.
     */
    public DexClassLoader(String dexPath, String optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
    

    区别就在于optimizedDirectory是否为null。具体可以看BaseDexClassLoader的构造方法

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(parent);
            this.originalPath = dexPath;
            this.pathList =
                new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    

    从这里我们找到了DexPathList类,那么还需要看看DexPathList是干嘛的

    public DexPathList(ClassLoader definingContext, String dexPath,
                String libraryPath, File optimizedDirectory) {
           ...
            this.dexElements =
                makeDexElements(splitDexPath(dexPath), optimizedDirectory);
    }
    /**
         * Makes an array of dex/resource path elements, one per element of
         * the given array.
         */
    private static Element[] makeDexElements(ArrayList<File> files,
                File optimizedDirectory) {
            ArrayList<Element> elements = new ArrayList<Element>();
            for (File file : files) {
                ...
                dex = loadDexFile(file, optimizedDirectory);
            }
            return elements.toArray(new Element[elements.size()])
    }
    private static DexFile loadDexFile(File file, File optimizedDirectory)
                throws IOException {
            if (optimizedDirectory == null) {
                return new DexFile(file);
            } else {
                String optimizedPath = optimizedPathFor(file, optimizedDirectory);
                return DexFile.loadDex(file.getPath(), optimizedPath, 0);
            }
        }
    ...
    /**
         * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
         * file with a "classes.dex" inside.
         *
         * The VM will generate the name of the corresponding file in
         * /data/dalvik-cache and open it, possibly creating or updating
         * it first if system permissions allow.  Don't pass in the name of
         * a file in /data/dalvik-cache, as the named file is expected to be
         * in its original (pre-dexopt) state.
         *
         * @param file
         *            the File object referencing the actual DEX file
         *
         * @throws IOException
         *             if an I/O error occurs, such as the file not being found or
         *             access rights missing for opening it
         */
    public DexFile(File file) throws IOException {
            this(file.getPath());
    }
    

    DexPathList主要是加载全部的dex文件,内部通过dexElements属性维护全部的dex
    我们还需要看一下DexPathList类的loadDexFile方法,里面有我们关心的一个参数optimizedDirectory,optimizedDirectory为null时,构造一个DexFile对象,不为null的时候,直接通过DexFile.loadDex(sourcePathName, outputPathName, flag)加载一个DexFile文件
    先来看看DexFile.loadDex方法

    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");
            //System.out.println("DEX FILE cookie is " + mCookie);
    }
    

    本质上还是调用的DexFile的构造方法,那么不同之处在哪,在于构造方法里面openDexFile传入的参数outputName,对于optimizedDirectory为null,传入的outputName为null,这是为什么呢,因为PathClassLoader加载的是已经安装的应用,这部分classes已经被optimized处理过了,位于/data/dalvik-cache/目录下,不需要在为outputName指定路径了,而对于DexClassLoader而言,加载的是没有安装过的dex文件,那么是需要将优化的classes存储到应用的私有目录。

    BootClassLoader: 继承的是ClassLoader类,作为PathClassLoader的父类加载器存在(也是Android中类加载器的根节点),也是一个package级别的类,对外获取不到

    2. 简述ClassLoader的双亲委托模型

    双亲委托模型,classLoader用于将一个class文件加载到虚拟机中,供程序运行时使用,动态加载。
    在加载class的过程中,首先会先判断父加载器有没有加载这个类,如果加载了,那么由父加载器返回这个class对象,如果没有的话,那么就由当前的类加载器加载。(父加载器也是按照这个过程来)

    这样做的好处是避免重复加载class,安全

    3. 简述双亲委托模型在热修复领域的应用

    首先我们需要看一下,在Android中,如何加载一个类,看看ClassLoader的loadClass方法

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
    ...
    c = findClass(name);
    ...
    }
    
    //对于android应用程序中加载class的加载器,我们确定无疑就是PathClassLoader,而PathClassLoader继承自BaseDexClassLoader,BaseDexClassLoader的findClass方法是重写过的
    @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            Class clazz = pathList.findClass(name);
            if (clazz == null) {
                throw new ClassNotFoundException(name);
            }
            return clazz;
        }
    

    pathList是什么呢,它的类型是DexPathList,DexPathList有一个成员变量叫做dexElements,维护了全部的dex文件,而我们要查找的类都是通过遍历这个dexElements数组,来获取到的。

    在热修复中,我们可以将修复后的bugFixdex插入到这个dexElements的最前面,那么在查找某一个类A(存在bug的话)的时候,就优先从bugFixdex中查找到,并返回,而不会使用dexElements里面原来有问题的类A了,这样就达到了修复bug的问题

    相关文章

      网友评论

          本文标题:Android中的ClassLoader

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