Android类加载机制

ClassLoader

Android 中使用PathClassLoaderDexClassLoader等来实现类的加载,这两个类都继承自BaseDexClassLoader,而BaseDexClassLoader继承自ClassLoader

从Android官方文档中可知:PathClassLoader用来加载系统类及已安装的应用程序的类;DexClassLoader用来加载jarapkdex文件中的类。

ClassLoader使用了一种称为“双亲委托”的机制:

public abstract class ClassLoader {

    private final ClassLoader parent;

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            Class c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    c = findClass(name);
                }
            }
            return c;
    }
}

调用loadClass方法来加载类时,首先使用findLoadedClass来获取已经被加载到内存中的类,如果类已经加载过,就会直接返回已加载的类;否则,先去调用parentloadClass方法来加载类,parent无法加载时才会调用自身的findClass方法来加载。

BaseDexClassLoader

public class BaseDexClassLoader extends ClassLoader {

    private final DexPathList pathList;

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

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = pathList.findClass(name);
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }
}

BaseDexClassLoaderfindClass方法将类的加载委托给DexPathList类型的pathList处理,下面看下DexPathList

DexPathList

DexPathList类关键方法如下:

final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";
    private static final String JAR_SUFFIX = ".jar";
    private static final String ZIP_SUFFIX = ".zip";
    private static final String APK_SUFFIX = ".apk";

    private final ClassLoader definingContext;
    private final Element[] dexElements;

    public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
        this.definingContext = definingContext;
        this.dexElements =
            makeDexElements(splitDexPath(dexPath), optimizedDirectory);
    }

    private static Element[] makeDexElements(ArrayList<File> files,
            File optimizedDirectory) {
        ArrayList<Element> elements = new ArrayList<Element>();

        for (File file : files) {
            ZipFile zip = null;
            DexFile dex = null;
            String name = file.getName();
            if (name.endsWith(DEX_SUFFIX)) {
                // Raw dex file (not inside a zip/jar).
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: " + file, ex);
                }
            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                    || name.endsWith(ZIP_SUFFIX)) {
                try {
                    zip = new ZipFile(file);
                } catch (IOException ex) {
                    System.logE("Unable to open zip file: " + file, ex);
                }
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ignored) {
                }
            } else {
                System.logW("Unknown file type for: " + file);
            }
            if ((zip != null) || (dex != null)) {
                elements.add(new Element(file, zip, dex));
            }
        }
        return elements.toArray(new Element[elements.size()]);
    }
}

DexPathList构造函数参数dexPath包含了dex文件的列表,这些dex文件可以是jarzipapk格式的压缩包,也可以直接是dex文件。DexPathList使用makeDexElements方法遍历这些dex文件,使用loadDexFile方法加载dex文件:

final class DexPathList {

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

loadDexFile方法则使用DexFileloadDex方法打开dex文件,同时会将dex优化为odex文件,返回的是DexFile对象,对dex文件的打开文件、加载类等操作都通过DexFile来实现。

DexPathList.makeDexElements将每个dex文件对应的FileZipFileDexFile包装成Element变量,这些Element变量存在DexPathListdexElements数组里。

/*package*/ static class Element {
    public final File file;
    public final ZipFile zipFile;
    public final DexFile dexFile;
    public Element(File file, ZipFile zipFile, DexFile dexFile) {
        this.file = file;
        this.zipFile = zipFile;
        this.dexFile = dexFile;
    }
    public URL findResource(String name) {
        if ((zipFile == null) || (zipFile.getEntry(name) == null)) {
            return null;
        }
        try {
            return new URL("jar:" + file.toURL() + "!/" + name);
        } catch (MalformedURLException ex) {
            throw new RuntimeException(ex);
        }
    }
}

DexPathList类里加载class如下:

final class DexPathList {

    public Class findClass(String name) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;
            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        return null;
    }
}

DexPathList加载类时,遍历Element数组,使用element.dexFileloadClassBinaryName方法来实际加载类:

public final class DexFile {
    public Class loadClassBinaryName(String name, ClassLoader loader) {
        return defineClass(name, loader, mCookie);
    }
    private native static Class defineClass(String name, ClassLoader loader, int cookie);

}

loadClassBinaryName方法最终调用的是 native 方法defineClass

总结

DexClassLoaderPathClassLoader加载类的过程:

DexClassLoader.loadClass ->
    DexClassLoader.findClass ->
        BaseDexClassLoader.findClass ->
            DexPathList.findClass ->
                DexFile.loadClassBinaryName ->
                    DexFile.defineClass

参考

DexClassLoader

DexClassLoader(source code)

PathClassLoader

PathClassLoader(source code)

BaseDexClassLoader(source code)

DexPathList

DexFile



—  我的个人空间 |   —