美文网首页
关于ClassLoader

关于ClassLoader

作者: 瑜小贤 | 来源:发表于2020-03-29 19:08 被阅读0次

    一. Java中的ClassLoader

    1. 继承关系

    • ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。
    • SecureClassLoader继承了抽象类ClassLoader,但SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
    • URLClassLoader继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
    • ExtClassLoaderAppClassLoader都继承自URLClassLoader,它们都是Launcher的内部类,Launcher是Java虚拟机的入口应用,ExtClassLoaderAppClassLoader都是在Launcher中进行初始化的。

    二. Android中的ClassLoader

    1. 继承关系

    • ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。BootClassLoader是它的内部类。
    • SecureClassLoader继承了抽象类ClassLoaderSecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
    • URLClassLoader类继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
    • InMemoryDexClassLoader是Android8.0新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件。
    • BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoaderDexClassLoader都继承它。

    2. 作用和分类

    (1)作用:

    Android虚拟机运行的是Dex字节码(将Class文件合并优化生成的产物),ClassLoader用来加载dex文件。

    (2)分类

    分为系统类加载器和自定义类加载器。主要的系统类加载器:

    • BootClassLoader
    • PathClassLoader
    • DexClassLoader

    三. Android中ClassLoader源码分析

    相关源码:
    /dalvik/system

    1. ClassLoader

    (1)构造函数:
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }
    

    ClassLoader需要传入一个父ClassLoader,如果父ClassLoader不传的时候,通过
    getSystemClassLoader方法,创建了一个PathClassLoader

    public static ClassLoader getSystemClassLoader() {
        return SystemClassLoader.loader;
    }
      static private class SystemClassLoader {
        public static ClassLoader loader = ClassLoader.createSystemClassLoader();
    }
    private static ClassLoader createSystemClassLoader() {
        String classPath = System.getProperty("java.class.path", ".");
        String librarySearchPath = System.getProperty("java.library.path", "");
        return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
    }
    
    (2)loadClass方法:
    protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException {
        // First, check if the class has already been loaded
        //(1)先判断这个类是否被加载过
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                //(2)如果没被加载,先让父加载器进行加载
                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
            }
            //(3)如果父加载器加载失败,则自身进行加载
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClass(name);
            }
        }
        return c;
    }
    

    双亲委托机制:

    1. 除了顶层的类加载器外,其他的类加载器都有自己的父类加载器,在加载类时首先判断这个类是否被加载过,如果已经加载则直接返回。
    2. 如果未被加载过,则先尝试让父加载器进行加载,最终所有加载请求都会传递给顶层的加载器中。
    3. 当父加载器发现未找到所需的类而无法完成加载请求时,子加载器的findClass方法中进行加载。

    2. BaseDexClassLoader

    (1)构造:
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        this(dexPath, librarySearchPath, parent, null, false);
    }
    
    public BaseDexClassLoader(String dexPath,
            String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
            boolean isTrusted) {
        super(parent);
        this.sharedLibraryLoaders = sharedLibraryLoaders == null
                ? null
                : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
        if (reporter != null) {
            reportClassLoaderChain();
        }
    }
    
    • dexPath:指目标类所在的APK或jar文件的路径,如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得。
    • optimizedDirectory:解压的dex文件存储路径,这个路径必须是一个内部存储路径,一般情况下使用当前应用程序的私有路径:/data/data//…。
    • librarySearchPath:指目标类中所使用的C/C++库存放的路径
      parent:父加载器。
    (2)DexPathList

    DexPathList中维护着一个Element数组,这个数组中Element元素就是Dex文件(做热修复就要在这里做文章了)

    #DexPathList
     /*package*/ static class Element {
     
        @UnsupportedAppUsage
        private final File path;
        /** Whether {@code path.isDirectory()}, or {@code null} if {@code path == null}. */
        private final Boolean pathIsDirectory;
        @UnsupportedAppUsage
        private final DexFile dexFile;
        private ClassPathURLStreamHandler urlHandler;
        private boolean initialized;
        
        @UnsupportedAppUsage
        public Element(DexFile dexFile, File dexZipPath) {
            if (dexFile == null && dexZipPath == null) {
                throw new NullPointerException("Either dexFile or path must be non-null");
            }
            this.dexFile = dexFile;
            this.path = dexZipPath;
            // Do any I/O in the constructor so we don't have to do it elsewhere, eg. toString().
            this.pathIsDirectory = (path == null) ? null : path.isDirectory();
        }
        public Element(DexFile dexFile) {
            this(dexFile, null);
        }
        public Element(File path) {
            this(null, path);
        }
        ......
    }
    

    3. BootClassLoader

    Android平台上所有ClassLoader的最终parent。Android系统启动时会使用BootClassLoader来预加载常用类。

    class BootClassLoader extends ClassLoader {
        private static BootClassLoader instance;
        @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
        public static synchronized BootClassLoader getInstance() {
            if (instance == null) {
                instance = new BootClassLoader();
            }
            return instance;
        }
    ...
    }
    

    BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问

    4. PathClassLoader

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
    
    @libcore.api.CorePlatformApi
    public PathClassLoader(
            String dexPath, String librarySearchPath, ClassLoader parent,
            ClassLoader[] sharedLibraryLoaders) {
        super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
    }
    

    使用PathClassLoader来加载系统类和应用程序的类,通常用来加载已安装的apk的dex文件
    构造函数没有optimizedDirectory,无法定义dex文件路径,该参数默认值为/data/dalvik-cache

    5. DexClassLoader

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
    

    DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),在其父类BaseDexClassLoader里对".jar",".zip",".apk",".dex"后缀的文件最后都会生成一个对应的dex文件。

    四. Android加载Class的过程

    1. BaseDexClassLoader的findClass

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // First, check whether the class is present in our shared libraries.
        if (sharedLibraryLoaders != null) {
            for (ClassLoader loader : sharedLibraryLoaders) {
                try {
                    return loader.loadClass(name);
                } catch (ClassNotFoundException ignored) {
                }
            }
        }
        // Check whether the class in question is present in the dexPath that
        // this classloader operates on.
        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;
    }
    

    内部调用了DexPathList的findClass方法

    #DexPathList
    private Element[] dexElements;
    ......
    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;
    }
    
    public Class<?> findClass(String name, ClassLoader definingContext,
            List<Throwable> suppressed) {
        return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                : null;
    }
    

    DexPathList内部包含了一个DexFile的数组dexElements。类加载的过程,就是遍历这个数组调用了DexFileloadClassBinaryName方法,最终调用native方法defineClassNative

    感谢https://blog.csdn.net/www851903307/article/details/87017662

    相关文章

      网友评论

          本文标题:关于ClassLoader

          本文链接:https://www.haomeiwen.com/subject/jliuuhtx.html