天天看點

07.源碼閱讀(ClassLoader類的加載機制)

在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就可以了

繼續閱讀