美文网首页
Android ClassLoader的前世今生

Android ClassLoader的前世今生

作者: feifei_fly | 来源:发表于2020-12-17 10:13 被阅读0次

    一直以来我对Classloader理解都比较模糊,究竟什么是ClassLoader。针对下面几点疑,对ClassLoader做了一下梳理,记录在此。

    • ClassLoader 是什么?它存在的使命是什么
    • ClassLoader的调用方式(为什么我们平时接触不到这个类)
    • ClassLoder 分类和继承关系
    • 什么是双亲代理机制
    • BootClassLoader、PatchClassLoader分别是什么时候创建的,各自分别会加载哪些类

    一、ClassLoader 做了什么事情,它为什么会存在

    我们知道Android app是运行在(dalvik/art)虚拟机中,而java程序运行在JVM虚拟机中。

    虚拟机 出现时机 特点
    jvm java 虚拟机
    dalvik android 5.0之前 JIT(just in time)
    art android 5.0之后 AOT(ahead of time)

    1、JVM 架构

    dalvik和art都是从jvm演化而来的,我们先从JVM的整体架构看起

    image

    VJM 整体被分为三个子系统:
    (1)类加载器子系统 (2)运行时数据区 (3)执行引擎

    1.1、 类加载器子系统

    image

    Java类的动态加载功能是由类加载器子系统处理。当它在运行时(不是编译时)首次引用一个类时,加载、链接并初始化该类文件。

    1.1.1、 类的加载由此组件完成,通常就是我们所说的classloader

    加载过程主要完成三件事情:

    • 通过类的全限定名来获取定义此类的二进制字节流
    • 将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
    • 在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口。
    1.1.3、 链接

    类文件被加载完之后,还不能使用,必须要经过链接的过程

    • 校验: 字节码校验器会校验生成的字节码是否正确,如果校验失败,我们会得到校验错误。
    • 准备: 为所有的静态变量,分配内存并初始化默认值。
    • 解析: 解析所有符号内存引用被方法区(Method Area)的原始引用所替代。
      举个例子来说明,在com.sbbic.Person类中引用了com.sbbic.Animal类,在编译阶段,Person类并不知道Animal的实际内存地址,因此只能用com.sbbic.Animal来代表Animal真实的内存地址。在解析阶段,JVM可以通过解析该符号引用,来确定com.sbbic.Animal类的真实内存地址(如果该类未被加载过,则先加载)。

    1.2、运行时数据区

    运行时数据区域被划分为5个主要组件:

    • 方法区 (线程共享) 常量 静态变量 JIT(即时编译器)编译后代码也在方法区存放
    • 堆内存(线程共享) 垃圾回收的主要场地
    • 程序计数器 当前线程执行的字节码的位置指示器
    • Java虚拟机栈(栈内存) :保存局部变量,基本数据类型以及堆内存中对象的引用变量
    • 本地方法栈 (C栈):为JVM提供使用native方法的服务


      image

    1.3、执行引擎

    分配给运行时数据区的字节码将由执行引擎执行。
    执行引擎读取字节码并逐段执行。

    1.3.1、解释器

    解释器能快速的解释字节码,但执行却很慢。 解释器的缺点就是,当一个方法被调用多次,每次都需要重新解释。

    1.3.2、编译器

    JIT编译器消除了解释器的缺点。执行引擎利用解释器转换字节码,但如果是重复的代码则使用JIT编译器将全部字节码编译成本机代码。本机代码将直接用于重复的方法调用,这提高了系统的性能。

    1.3.3、垃圾回收器

    收集并删除未引用的对象。可以通过调用"System.gc()"来触发垃圾回收,但并不保证会确实进行垃圾回收。

    1.3.4、Java本地接口 (JNI)

    本地接口的作用是为了融合不同的编程语言为Java所用。它的初衷是为了融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须要有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载加载native libraries

    小结:ClassLoader存在的意义

    编译完的class存储在磁盘上,必须加载到内存,经过链接和初始化之后,才能被使用。ClassLoader 就是负责完成将类从磁盘文件(.class、.dex)加载到内存中的。
    java中类是动态加载的,一个类在被使用之前,才从磁盘加载到内存中,经过链接和初始化之后被使用。
    出于效率的考虑,系统会提前将一部分常用的类提前加载到内存中,剩下的class 随用随加载(动态加载)

    二、类的加载方式

    既然ClassLoader是每个类被使用前的必经之路,为什么我们平时基本接触不到ClassLoader呢?

    类的状态分为隐式装载和显示装载两种方式。大部分场景都会采用隐式加载的方式来加载class。

    一般我们调用new 新建对象时,或调用类的静态属性/方法时 都会触发类的隐式加载。这是有JVM自动完成的,我们感知不到。

    隐式加载

    new Student()
    

    但是一些特殊情况,我们利用反射来使用类时,就会通过Class.forname()等方法显式加载一个类
    显式加载

    Class.forname("类的全限定名")
    Classloader.loadClass("类的全限定名")
    

    三、Android中的ClassLoader 有哪些?

    Android中ClassLoader主要有四个类:ClassLoader、BootClassLoader、BaseDexClassLoader、PathClassLoader、DexClassLoader


    image

    SecureClassLoader 和URLClassLoader 是将.jar 加载成class,在Android中无法直接加载.jar,所以SecureClassLoader和URLClassLoader 没有用。

    • ClassLoader 类加载器的基类,是抽象类,不能直接使用。其子类加载器 一般复写其findClass()方法,实现差异化的类加载。

    • BootClassLoader 启动加载器它ClassLoader的内部类。这个内部类是包内可见,所以我们没法使用。它是Android平台上所有ClassLoader的最终parent,Android系统启动时会使用BootClassLoader来预加载常用类。

    • BaseDexClassLoader是Android中ClassLoader的主要实现类,是PathClassLoader和DexClassLoader的父类。但不直接使用。对外使用PathClassLoader和DexClassLoader

    • PathClassLoader 负责Framework系统类和应用类的加载

    • DexClassLoader 可以加载dex文件以及包含dex的压缩文件(apk和jar文件),可以加载sdcard上的类文件。

    3.1、ClassLoader

    ClassLoader 是一个抽象类,其他所有ClassLoader的基类

    以下是JVM 中ClassLoader的一个介绍,Android虚拟机dalvik/art 是从JVM演进而来的,大概思想是一直的。

    ClassLoader主要有四个主要方法
    - defineClass(byte[] , int ,int),将byte字节流解析为JVM能够识别的Class对象(直接调用这个方法生成的Class对象还没有resolve,这个resolve将会在这个对象真正实例化时resolve)
    - resolveClass(),手动调用这个使得被加到JVM的类被链接
    - loadClass() 运行时可以通过调用此方法动态加载一个类
    - findClass() 通过类名去加载对应的Class对象,当我们实现自定义的classLoader通常是重写这个方法,根据传入的类名找到对应字节码的文件,并通过调用defineClass解析出Class对象。
    

    我们看一下Android中的ClassLoader

    public abstract class ClassLoader{
      @Deprecated
        protected final Class<?> defineClass(byte[] b, int off, int len)
            throws ClassFormatError
        {
            throw new UnsupportedOperationException("can't load this type of class file");
        }
    
    
       protected final void resolveClass(Class<?> c) {
        }
        
           @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            return Class.classForName(name, false, null);
        }
        
         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;
        }
    }
    

    在Android的ClassLoader中,

    • defineClass()已经被废弃,
    • resolveClass()方法里面为空(由子类实现?)
    • findClass() 直接调用了Class.classForName()方式
    ##Class.java
    @FastNative
    static native Class<?> classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;
    
    • loadClass() 实现了ClassLoader双亲代理的主要逻辑
     protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
                // First, check if the class has already been loaded
                //(1)查看当前的ClassLoader 有没有加载过此类,如果加载过,则直接返回Class
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        if (parent != null) {
                            //(2)当前的ClassLoader未加载过该Class,则交给父ClassLoader去加载clsss
                            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.
                        //(3)最后才是 自己尝试加载class
                        c = findClass(name);
                    }
                }
                return c;
        }
    

    3.1.1、双亲代理模式:

    双亲代理模式分三个过程:ClassLoader在加载字节码的时候,首先会询问当前的classloader是否已经载 过子类,如果已经加载过,直接返回不再重复加载。如果没有查询parent 是否加载过子类,如果加载过那么返回parent加载过的字节码文件。如果没有加载过,最终再由子classloader去完成真正的加载。

    双亲代理模式的优点:

    提高类加载的效率.

    这个过程的好处就是如果我们一个类被树中任意一个classloader节点加载过,那么我们在以后的整个生命周期中都不会再被去重新的加载,大大提高了类加载的效率。

    类加载的共享功能

    比如我们的framework层级的类,一旦被我们顶层的classLoader加载过可以缓存在内存里面,以后任何地方用到,都不需要重新加载

    类加载隔离功能

    由不同Classloader加载的类肯定不是同一个类。可以避免用户自己写一些代码冒充Android系统中的核心类库,举例:一些系统层级的类会在初始化的时候就被加载,如java.lang.string在应用启动之前就被系统加载好的,如果在一个应用里面能够简单的用自己定义的string类把系统的替换掉,那就存在很大的安全问题。

    备注:

    • java中 两个类相同 = 包名相同+类名相同+加载该类的Classloader相同。

    • Android中的所有子ClassLoader 没有复写loadCass()方法,是为了维持Class加载双亲代理的原则

    3.2、BootClassLoader

    BootClassLoader 是所有最顶端的ClassLoader,所以其复写了loadClass()方法,没有双亲代理的过程。

    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;
        }
    
        public BootClassLoader() {
            super(null);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            return Class.classForName(name, false, null);
        }
    
    
        @Override
        protected Class<?> loadClass(String className, boolean resolve)
               throws ClassNotFoundException {
            Class<?> clazz = findLoadedClass(className);
    
            if (clazz == null) {
                clazz = findClass(className);
            }
    
            return clazz;
        }
    
        @Override
        public Enumeration<URL> getResources(String resName) throws IOException {
            return findResources(resName);
        }
    }
    
    

    3.3、BaseDexClassLoader

    BaseDexClassLoader 是android 中ClassLoader系统中的基石,它实现了类的查找和加载过程,是PathClassLoader和DexClassLoader的基类。

       public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String librarySearchPath, ClassLoader parent, boolean isTrusted){
                    
                }
    

    BaseDexClassLoader的构造函数包含以下几个重要参数

    • dexPath:指目标类所在的apk、jar、dex文件的路径。类装载器将从该路径中寻找指定的目标类,类必须是apk、zip或dex的全路径。如果要包含多个路径,路径之间必须使用特定的分割符分隔。上面"支持加载APK、DEX和JAR“,也可以从SD卡进行加载。
    • File optimizedDirectory 由于dex文件被包含在apk或者Jar文件中,因此在装载目标类之前需要先从apk或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径。这也是对apk中dex根据平台进行ODEX优化的过程。其实apk是一个程序压缩包,里面包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件。 但是android-26之后,这个参数已经被废弃,没有任何作用了。我猜测系统维护了一块区域,才存储解压后的odex文件,不需要用户指定了。
    • librarySearchPath: 指目标类中所使用的C/C++库存放的路径
    • parent :父类加载器

    BaseDexClassLoader 是如何在本类中查找类的。

    BaseDexClassLoader的findClass()方法 会把查找dex的任务委托给DexPathList类来实现。

    public class BaseDexClassLoader extends ClassLoader {
        // 需要加载的dex列表
        private final DexPathList pathList;
        // dexPath要加载的dex文件所在的路径,optimizedDirectory是odex将dexPath
        // 处dex优化后输出到的路径,这个路径必须是手机内部路劲,libraryPath是需要
        // 加载的C/C++库路径,parent是父类加载器对象
        public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
            // 使用pathList对象查找name类
            Class c = pathList.findClass(name, suppressedExceptions);
            return c;
        }
    }
    

    查看DexPathList的实现代码,在内部会类加载器加载路径查找dex文件,然后将它们解析成Element对象.Element对象代表的是dex文件或资源文件,它里面保存了文件对象。

    static class Element {
        private final File file;
        private final boolean isDirectory;
        private final File zip;
        private final DexFile dexFile;
    
        private ZipFile zipFile;
        private boolean initialized;
    
        // file文件,是否是目录,zip文件通常都是apk或jar文件,dexFile就是.dex文件
        public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
            this.file = file;
            this.isDirectory = isDirectory;
            this.zip = zip;
            this.dexFile = dexFile;
        }
    }
    

    在DexPathList类构造的时候会首先将dexPath变量内容分隔成多个文件路径,并且根据路径查找Android中的dex和资源文件,将它们解析后存放到Element数组中。

    /*package*/ final class DexPathList {
        private static final String DEX_SUFFIX = ".dex";
        private final ClassLoader definingContext;
        // 
        private final Element[] dexElements;
        // 本地库目录
        private final File[] nativeLibraryDirectories;
    
        public DexPathList(ClassLoader definingContext, String dexPath,
                String libraryPath, File optimizedDirectory) {
            // 当前类加载器的父类加载器
            this.definingContext = definingContext;
            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
            // 根据输入的dexPath创建dex元素对象
            this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                               suppressedExceptions);
            if (suppressedExceptions.size() > 0) {
                this.dexElementsSuppressedExceptions =
                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
            } else {
                dexElementsSuppressedExceptions = null;
            }
            this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
        }
    }
    

    makeDexElements就是把前面dexPath里面解析到的路径下的文件全部遍历一遍,如果是dex文件或apk和jar文件就会查找它们内部的dex文件,将所有这些dex文件都加入到Element数组中,完成加载路径下面的所有dex解析。

    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
        ArrayList<Element> elements = new ArrayList<Element>();
        // 所有从dexPath找到的文件
        for (File file : files) {
            File zip = null;
            DexFile dex = null;
            String name = file.getName();
            // 如果是文件夹,就直接将路径添加到Element中
            if (file.isDirectory()) {
                elements.add(new Element(file, true, null, null));
            } else if (file.isFile()){
                // 如果是文件且文件名以.dex结束
                if (name.endsWith(DEX_SUFFIX)) {
                    try {
                        // 直接从.dex文件生成DexFile对象
                        dex = loadDexFile(file, optimizedDirectory);
                    } catch (IOException ex) {
                        System.logE("Unable to load dex file: " + file, ex);
                    }
                } else {
                    zip = file;
    
                    try {
                        // 从APK/JAR文件中读取dex文件
                        dex = loadDexFile(file, optimizedDirectory);
                    } catch (IOException suppressed) {
                        suppressedExceptions.add(suppressed);
                    }
                }
            } else {
                System.logW("ClassLoader referenced unknown path: " + file);
            }
    
            if ((zip != null) || (dex != null)) {
                elements.add(new Element(file, false, zip, dex));
            }
        }
    
        return elements.toArray(new Element[elements.size()]);
    }
    

    3.4、PathClassLoader

    PathClassLoader继承自BaseDexClassLoader,实现也都在BaseDexClassLoader中。PathClassLoader 默认从/data/dalvik-cache 读取dex

    public class PathClassLoader extends BaseDexClassLoader {
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super(dexPath, null, librarySearchPath, parent);
        }
    }
    

    Android系统中的ClassLoader 默认都是以PathClassLoader为父ClassLoader。

    3.5、DexClassLoader

    DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk

        //dexPath :dex路径
        //optimizedDirectory :制定输出dex优化后的odex文件,可以为null
        //libraryPath:动态库路径(将被添加到app动态库搜索路径列表中)
        //parent:制定父类加载器,以保证双亲委派机制从而实现每个类只加载一次。
        public DexClassLoader(String dexPath, String optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
        }
    

    3.6、DexClassLoader 与PathClassLoader的区别

    DexClassLoader和与PathClassLoader 都是BaseDexClassLoader的子类,主要功能都在BaseDexClassLoader中,DexClassLoader比PathClassLoader 多了一个optimizedDirectory参数。

    但是从android-26(8.0)开始 BaseDexClassLoader中optimizedDirectory 被废弃了,没有用了。

      /**
         * 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 since API level 26.
         * @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) {
            this(dexPath, librarySearchPath, parent, null, false);
        }
    

    因此 在android-16之前

    PathClassLoader只能加载系统安装的apk,而DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk
    

    而在android-26之后 PathClassLoader和DexCassLoader 没有区别,两者都能加载dex,jar和apk,也都能加载sdcard上未安装的apk

    3.7、classLoader的继承关系

    一个普通android程序启动后,它会用到哪些ClassLoader 谁是谁的父ClassLoader

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            ClassLoader loader = this.getClassLoader();
            while (loader != null){
                Log.d("test","Class_Loader"+loader);
                loader = loader.getParent();
            }
    
    
        }
    
    }
    

    输出内容:

    2020-12-11 16:41:01.941 8154-8154/com.sogou.iot.testtouch1 D/test: Class_Loaderdalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sogou.iot.testtouch1-sojMcBcZW1_0iU5Fv9sK6g==/base.apk"],nativeLibraryDirectories=[/data/app/com.sogou.iot.testtouch1-sojMcBcZW1_0iU5Fv9sK6g==/lib/arm64, /system/lib64, /vendor/lib64]]]
    2020-12-11 16:41:01.941 8154-8154/com.sogou.iot.testtouch1 D/test: Class_Loaderjava.lang.BootClassLoader@847820d
    

    所以一个常规Android应用只会用到两个ClassLoader

    PathClassLoader->BootClassLoader

    小彩蛋:

    • Framework和系统App dex-opt解压的odex文件 会保存在/data/dalvik-cache目录
    S1 AI Recorder:/data/dalvik-cache/arm64 # ls
    system@app@ApnSettingsPlugin@ApnSettingsPlugin.apk@classes.art 
    system@app@ApnSettingsPlugin@ApnSettingsPlugin.apk@classes.dex 
    system@app@ApnSettingsPlugin@ApnSettingsPlugin.apk@classes.vdex 
    system@app@Bluetooth@Bluetooth.apk@classes.art                 
    system@app@Bluetooth@Bluetooth.apk@classes.dex                 
    system@app@Bluetooth@Bluetooth.apk@classes.vdex                
    system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.art
    system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.dex
    system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.vdex
    system@app@BrowserXposed@BrowserXposed.apk@classes.art         
    system@app@BrowserXposed@BrowserXposed.apk@classes.dex         
    system@app@BrowserXposed@BrowserXposed.apk@classes.vdex   
    
    • 使用adb install 安装的普通app,解压的文件会保存在/data/app/package/oat/arm64/目录下
    S1 AI Recorder:/data/app/com.sogou.iot.testtouch1-sojMcBcZW1_0iU5Fv9sK6g==/oat/arm64 # ls -l
    total 2592
    -rw-r--r-- 1 system all_a81   45696 2020-12-11 16:40 base.odex
    -rw-r--r-- 1 system all_a81 2602050 2020-12-11 16:41 base.vdex
    

    四、BootClassLoader和PathClassLoader是什么时候加载的。

    4.1、 BootClassLoader 最早是在何时被创建的

    从Zygote进程开始说起

    Zygote进程启动时 会调用ZygoteInit.main()方法,调用preload()方法,最终调会用preloadClasses()

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    
    public static void main(String argv[]) {
       ...
            try {
                 ...
                    preload(bootTimingsTraceLog);
                 ... 
            }
        }
    

    preloadClasses()从/system/etc/preloaded-classes文件中读取需要预加载的类,逐个调用Class.forName进行加载。

    private static void preloadClasses() {
            final VMRuntime runtime = VMRuntime.getRuntime();
            InputStream is;
            try {
                is = new FileInputStream(PRELOADED_CLASSES);//1
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
                return;
            }
            ...
            try {
                BufferedReader br
                    = new BufferedReader(new InputStreamReader(is), 256);//2
    
                int count = 0;
                String line;
                while ((line = br.readLine()) != null) {//3
                    line = line.trim();
                    if (line.startsWith("#") || line.equals("")) {
                        continue;
                    }
                      Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                    try {
                        if (false) {
                            Log.v(TAG, "Preloading " + line + "...");
                        }
                        Class.forName(line, true, null);//4
                        count++;
                    } catch (ClassNotFoundException e) {
                        Log.w(TAG, "Class not found for preloading: " + line);
                    } 
            ...
            } catch (IOException e) {
                Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
            } finally {
                ...
            }
    

    /system/etc/preloaded-classes文件中,预置了一下需要提前加载的类,如下

    android.app.ApplicationLoaders
    android.app.ApplicationPackageManager
    android.app.ApplicationPackageManager$OnPermissionsChangeListenerDelegate
    android.app.ApplicationPackageManager$ResourceName
    android.app.ContentProviderHolder
    android.app.ContentProviderHolder$1
    android.app.ContextImpl
    android.app.ContextImpl$ApplicationContentResolver
    android.app.DexLoadReporter
    android.app.Dialog
    android.app.Dialog$ListenersHandler
    android.app.DownloadManager
    android.app.Fragment
    

    Class.forName()首次被调用,对BootClassLoader进行了初始化。
    libcore/ojluni/src/main/java/java/lang/Class.java

    
      @CallerSensitive
        public static Class<?> forName(String name, boolean initialize,
                                       ClassLoader loader)
            throws ClassNotFoundException
        {
            if (loader == null) {
                loader = BootClassLoader.getInstance();//1
            }
            Class<?> result;
            try {
                result = classForName(name, initialize, loader);//2
            } catch (ClassNotFoundException e) {
                Throwable cause = e.getCause();
                if (cause instanceof LinkageError) {
                    throw (LinkageError) cause;
                }
                throw e;
            }
            return result;
        }
    

    4.2、PathClassLoader 最早在何时被创建

    PathClassLoader的创建也是从Zygote进程说起。
    Zygote进程启动SyetemServer进程时会调用ZygoteInit的handleSystemServerProcess方法,

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

     private static void handleSystemServerProcess(
                ZygoteConnection.Arguments parsedArgs)
                throws Zygote.MethodAndArgsCaller {
    
        ...
            if (parsedArgs.invokeWith != null) {
               ...
            } else {
                ClassLoader cl = null;
                if (systemServerClasspath != null) {
                    cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);//1
                    Thread.currentThread().setContextClassLoader(cl);
                }
                ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
            }
        }
        
         static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
          String libraryPath = System.getProperty("java.library.path");
          return PathClassLoaderFactory.createClassLoader(classPath,
                                                          libraryPath,
                                                          libraryPath,
                                                          ClassLoader.getSystemClassLoader(),
                                                          targetSdkVersion,
                                                          true /* isNamespaceShared */);
        }
    

    PathClassLoaderFactory的createClassLoader方法,创建出第一个PathClassLoader

    public static PathClassLoader createClassLoader(String dexPath,
                                                        String librarySearchPath,
                                                        String libraryPermittedPath,
                                                        ClassLoader parent,
                                                        int targetSdkVersion,
                                                        boolean isNamespaceShared) {
            PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
          ...
            return pathClassloader;
        }
    

    由此可知:

    • BootClassLoader 主要加载一些常用的需要预加载的类
    • PathClassLoader 负责Android FrameWork 和App中的类的加载

    五、参考文章:

    https://www.jianshu.com/p/52c38cf2e3d4

    https://blog.csdn.net/itachi85/article/details/78276837

    https://blog.csdn.net/renjingjingya0429/article/details/88525915

    插件化原理

    相关文章

      网友评论

          本文标题:Android ClassLoader的前世今生

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