ClassLoader工作机制

类加载器 ClassLoader

Android 的 Dalvik/ART 虚拟机如同标准 JAVA 的 JVM 虚拟机一样,在运行程序时首先需要将对应的类加载到内存中。因此,我们可以利用这一点,在程序运行时手动加载 Class,从而达到代码动态加载可执行文件的目的。

有几个 ClassLoader 实例?

在 Android 系统启动的时候会创建一个 Boot 类型的 ClassLoader 实例,用于加载一些系统 Framework 层级需要的类,我们的 Android 应用里也需要用到一些系统的类,所以 APP 启动的时候也会把这个 Boot 类型的 ClassLoader 传进来。

protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);
    ClassLoader classLoader = getClassLoader();
    if (classLoader != null){
        Log.i(TAG, "[onCreate] classLoader " + i + " : " + classLoader.toString());
        while (classLoader.getParent()!=null){
            classLoader = classLoader.getParent();
            Log.i(TAG,"[onCreate] classLoader " + i + " : " + classLoader.toString());
        }
    }
}

输出结果

[onCreate] classLoader 1 : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/me.kaede.anroidclassloadersample-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]]
[onCreate] classLoader 2 : java.lang.BootClassLoader@14af4e32

可以看见有 2 个 Classloader 实例,一个是 BootClassLoader(系统启动的时候创建的),另一个是 PathClassLoader(应用启动时创建的,用于加载 “/data/app/me.kaede.anroidclassloadersample-1/base.apk” 里面的类)。由此也可以看出,一个运行的 Android 应用至少有 2 个 ClassLoader。

创建 ClassLoader

/**
     * Constructs a new instance of this class with the specified class loader
     * as its parent.
     *
     * @param parentLoader
     *            The {@code ClassLoader} to use as the new class loader's
     *            parent.
     */
    protected ClassLoader(ClassLoader parentLoader) {
        this(parentLoader, false);
    }

构造函数需要传递一个parentLoader, 这样整个 Android 系统里所有的 ClassLoader 实例都会被一棵树关联起来, 也是 ClassLoader 的 双亲代理模型(Parent-Delegation Model)的特点。 这样做的目的是防止类被多次加载。

加载类

protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {  
        // 先判断是否被当前classLoader加载过
        Class<?> clazz = findLoadedClass(className);

        if (clazz == null) {
            ClassNotFoundException suppressed = null;
            // 判断Parent classLoader 是否加载过该类
            try {
                clazz = parent.loadClass(className, false);
            } catch (ClassNotFoundException e) {
                suppressed = e;
            }

            if (clazz == null) {
                try {
                   // 开始加载类
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }

        return clazz;
    }

BaseDexClassLoader#findClass

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

DexPathList#findClass

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

这样做有个明显的特点,如果一个类被位于树根的 ClassLoader 加载过,那么在以后整个系统的生命周期内,这个类永远不会被重新加载。这样也导致了基于<类加载方案 底层替换热修复方案必须得重启才能生效>

Android 中classLoader

  • DexClassLoader 可以加载 jar/apk/dex,可以从 SD 卡中加载未安装的 apk;
  • PathClassLoader 只能加载系统中已经安装过的 apk;

参考文档: