07.ソース読解(ClassLoaderクラスのロードメカニズム)

6292 ワード

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 binary name.
     * 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 loadClass} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a ClassNotFoundException.
     *
     * @param  name
     *         The binary name of the class
     *
     * @return  The resulting Class object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

ここを見つけて、見るものがないようで、注釈の中に1行あることに気づいた.
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 suppressedExceptions = new ArrayList();
       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 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 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を取得するのに便利である.
ここまでのクラスのロードメカニズムは基本的に理解されているが、クラスがロードされる必要がある場合、ClassLoaderによってDexFile配列からクラスのClassを取り出し、このクラスのオブジェクトを反射して取得すると、クラスにバグが存在する場合、正しいクラスのclassファイルまたはDexFileをこの配列の一番前に挿入するだけで、保証クラスはロード時に正しいclassにロードすればいいです