Android ClassLoader初识

概述

一个ClassLoader对象负责加载类。ClassLoader是一个抽象类。当给定一个类的二进制名称时,ClassLoader会尝试 查找生成 构成该类定义的数据(Class对象)。一种典型的策略是将 类名 转换成文件名,然后从文件系统中读取该名称的类文件。

每一个Class对象都包含一个定义它的ClassLoader对象。 Class#getClassLoader()
数组类的类对象不是被ClassLoader创建,而是根据Java运行时的要求自动创建的。数组类的ClassLoader和其元素类型的ClassLoader相同,如果元素类型是原始类型,则数组类没有ClassLoader。

ClassLoader类使用委托模型搜索 类和资源,每一个ClassLoader对象都有一个关联的父ClassLoader。当查找 类和资源 时,ClassLoader实例首先会将对 类和资源 的搜索委托给其 父ClassLoader,然后再尝试自己查找 类和资源。BootClassLoader是虚拟机内置的ClassLoader,本身没有 父ClassLoader,可以作为其他ClassLoader实例的 parent。

通常情况下,Java虚拟机会以平台相关的方式从本地文件系统加载类,但是有 不是源自文件的,可能有其它来源,例如网络,也可能由应用程序构造。ClassLoader#defineClass 可以将字节数组转换成Class对象。

类的二进制名称:
java.lang.Strinng
javax.swing.JSpinner$DefaultEditor
java.net.URLClassLoader$3$1

ClassLoaderUML类图
ClassLoader家族UML图
ClassLoader代码
public abstract class 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;
    }
    /**
     * 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);
    }
    ...
}

重点看 loadClass 的方法实现,使用了 Template Method 设计模式。定义了查找类的主体算法逻辑, findClass 可以不同具体实现。例如可以从文件、网络、内存中寻找。loadClass 主体逻辑很简单:

1、查询 会否已加载(从自己ClassLoader中查找),如果已加载直接返回。
2、如果未加载,委托给 父ClassLoader 去查询。父ClassLoader又会 执行一遍同样的 loadClass 逻辑
3、如果从来都没有加载过,会执行 findClass去创建 Class 对象。

BaseDexClassLoader代码

是一个基于dex的 一个常用功能的ClassLoader实现的 基类
还是看原文吧,怎么翻译都很怪异。
Base class for common functionality between various dex-based ClassLoader implementations.

public class BaseDexClassLoader extends ClassLoader {
    ...

    private final DexPathList pathList;

    /**
     * Constructs an instance.
     * Note that all the *.jar and *.apk files from {@code dexPath} might be
     * first extracted in-memory before the code is loaded. This can be avoided
     * by passing raw dex files (*.dex) in the {@code dexPath}.
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android.
     * @param optimizedDirectory this parameter is deprecated and has no effect
     * @param librarySearchPath the list of directories containing native
     * libraries, delimited by {@code File.pathSeparator}; may be
     * {@code null}
     * @param parent the parent class loader
     */
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);

        if (reporter != null) {
            reporter.report(this.pathList.getDexPaths());
        }
    }

    //dexFile must be an in-memory representation of a full dexFile.
    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
        // TODO We should support giving this a library search path maybe.
        super(parent);
        this.pathList = new DexPathList(this, dexFiles);
    }

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

BaseDexClassLoader#findClass 的最终的实现是 DexPathList #findClass

DexPathList代码
final class DexPathList {
    ...
    private final ClassLoader definingContext;
    /**
     * List of dex/resource (class path) elements.
     * Should be called pathElements, but the Facebook app uses reflection
     * to modify 'dexElements' (http://b/7726934).
     */
    private Element[] dexElements;

    /** List of native library path elements. */
    private final NativeLibraryElement[] nativeLibraryPathElements;

    /** List of application native library directories. */
    private final List nativeLibraryDirectories;

    /** List of system native library directories. */
    private final List systemNativeLibraryDirectories;

    //重点
    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;
    }
}

static class Element {
        /**
         * A file denoting a zip file (in case of a resource jar or a dex jar), or a directory
         * (only when dexFile is null).
         */
        private final File path;

        private final DexFile dexFile;

        private ClassPathURLStreamHandler urlHandler;
        private boolean initialized;
        ...
        public Class findClass(String name, ClassLoader definingContext,
                List suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }
      ...
}

DexPathList 中维护了一个数组 Element[] dexElements ,这个数组是一个dex资源的集合。(注意看dexElements 的注释,很有意思) findClass的时候,遍历 dexElements寻找Class,如果找到就返回,否则继续循环,如果最终没找到哦,则返回null。也就是说同一个类,如果两个dex文件都包含,则先取到哪个dex文件,就从这个文件中装载,热修复就可以从这里入手,修复异常的java类

其它

PathClassLoader 和 DexClassLoader 没有具体逻辑实现,没看出有什么区别。
加载系统framework的类使用的 BootClassLoader、加载应用自己编写的类用的 PathClassLoader

Log.e("MainActivity", "" + String.class.getClassLoader());
Log.e("MainActivity", "" + MainActivity.class.getClassLoader());
2020-05-08 15:36:02.782 31478-31478/com.jxf.androidhotfix E/MainActivity: java.lang.BootClassLoader@8046010
2020-05-08 15:36:02.783 31478-31478/com.jxf.androidhotfix E/MainActivity: dalvik.system.PathClassLoader[DexPathList[[zip file "/sdcard/patch.jar", zip file "/data/app/com.test.androidhotfix-da67YM0aVlq3wyo_3rK_rw==/base.apk"],nativeLibraryDirectories=[/data/app/com.test.androidhotfix-da67YM0aVlq3wyo_3rK_rw==/lib/arm64, /system/lib64, /system/vendor/lib64]]]

你可能感兴趣的:(Android ClassLoader初识)