美文网首页
07.源码阅读(ClassLoader类的加载机制)

07.源码阅读(ClassLoader类的加载机制)

作者: 任振铭 | 来源:发表于2018-03-30 16:02 被阅读77次

在Activity的启动流程中,我们知道最终Activity是通过ClassLoader加载的

public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        //加载得到Class然后反射得到对象
        return (Activity)cl.loadClass(className).newInstance();
    }

进入ClassLoader中

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

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;
    }

findLoadedClass,这个方法最终是通过native实现的,所以我们重点关注findClass

protected final Class<?> findLoadedClass(String name) {
        ClassLoader loader;
        if (this == BootClassLoader.getInstance())
            loader = null;
        else
            loader = this;
        return VMClassLoader.findLoadedClass(loader, name);
    }

@FastNative
    native static Class findLoadedClass(ClassLoader cl, String name);

findClass

/**
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

找到这里,似乎没有东西可以看了,注意到注释中有一行

This method should be overridden by class loader implementations that
follow the delegation model for loading classes

所以我们应该去ClassLoader的子类中去找这个方法

PathClassLoader --> BaseDexClassLoader -->ClassLoader

在BaseDexClassLoader中找到findClass方法

@Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
       List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
       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;
    }

pathList

this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);

来到DexPathList中,可以看到是遍历了dexElements去获取class

/**
     * Finds the named class in one of the dex files pointed at by
     * this instance. This will find the one in the earliest listed
     * path element. If the class is found but has not yet been
     * defined, then this method will define it in the defining
     * context that this instance was constructed with.
     *
     * @param name of class to find
     * @param suppressed exceptions encountered whilst finding the class
     * @return the named class or {@code null} if the class is not
     * found in any of the dex files
     */
    public Class<?> findClass(String name, List<Throwable> suppressed) {
       for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
               return clazz;
            }
       }

       if (dexElementsSuppressedExceptions != null) {
           suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
       }
       return null;
    }

我们看看这个dexElements是什么

this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);

private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
286            List<IOException> suppressedExceptions) {
287        Element[] elements = new Element[dexFiles.length];
288        int elementPos = 0;
289        for (ByteBuffer buf : dexFiles) {
290            try {
291                DexFile dex = new DexFile(buf);
292                elements[elementPos++] = new Element(dex);
293            } catch (IOException suppressed) {
294                System.logE("Unable to load dex file: " + buf, suppressed);
295                suppressedExceptions.add(suppressed);
296            }
297        }
298        if (elementPos != elements.length) {
299            elements = Arrays.copyOf(elements, elementPos);
300        }
301        return elements;
302    }

dexElements是一个数组,数组中存放了封装了DexFile的Element对象,便利数组Element中的DexFile中获取Class,可见,Class最终是从一个数组中取出来的

到这里类的加载机制基本上我们了解了,当一个类需要被加载的时候,是通过ClassLoader从一个DexFile数组中取出这个类的Class,然后反射获取到这个类的对象的,那么这就可以提供给我们一种热修复的思路,当一个类中存在bug,我们只需要将正确的类的class文件或者说DexFile插入到这个数组的最前边,保证类在加载的时候加载到正确的class就可以了

相关文章

网友评论

      本文标题:07.源码阅读(ClassLoader类的加载机制)

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