转载

Android进阶知识:类加载相关

类加载原理作为程序运行的基础,一直在程序的背后默默的付出。如今 Android 中的插件化、热修复等动态加载技术的实现也都涉及到了类加载的原理。关于类加载的相关知识我以前也是遇到一点看一点,没有完整的详细的了解过,最近有时间专门对这块知识进行了学习,于是这里做一个总结。

2. 类加载过程

一个类从 .class 文件被加载到内存,到在内存中使用,最后从内存中卸载,这是一个完整的生命周期过程。不过在获得 .class 文件之前,我们编码时的文件格式还是 .java 文件格式,还记得刚学 Java 时学到过在完成编码之后要先执行 javac 命令进行编译,编译生成对应的 .class 文件,之后再通过 java 命令执行 Java 程序。不过当时只知道是先编译再运行,并不知道到底是怎么运行的谁去运行的。

其实一个类从 .class 文件被加载到内存到从内存中卸载,整个生命周期一共经过以下几个阶段:

  1. 加载
  2. 连接(包含验证、准备、解析三个阶段)
  3. 初始化
  4. 使用
  5. 卸载
Android进阶知识:类加载相关
类加载生命周期

2.1 加载阶段

在加载阶段虚拟机主要完成以下三件事情:

java.lang.Class

这个简单的来说加载阶段主要就是将类的 .class 文件作为二进制字节流读入内存,并且在内存中实例化一个 java.lang.Class 对象以便后续访问。在这个阶段中 .class 文件二进制字节流的读取来源没有太多限制,可以非常灵活。比如可以从本地系统中读取、可以从 jar 包中读取、可以从网络下载等等。

2.2 连接—验证阶段

验证是连接阶段中的第一步,主要的作用是保证 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。在验证阶段大致会完成以下四个检验操作:

Class
Java

从以上几个操作可以看出,这个阶段主要就是将二进制字节流进行一个合法验证,包括文件格式、语义、数据流控制流和符号引用等。保证不会出现类似文件格式错误、继承了被 final 修饰的类、指令跳转错误、类型转换错误、修饰符访问性等等错误情况。

2.3 连接—准备阶段

准备阶段中主要是为类中静态变量在方法区里分配内存并且设置类变量的初始值。这里的初始值即零值。具体如下:

数据类型 零值
int 0
long 0L
short (short)0
char '/u0000'
byte (byte)0
boolean false
float 0.0f
double 0.0d
reference null

2.4 连接—解析阶段

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调试限定符7类符号引用进行。

2.5 初始化阶段

初始化阶段中真正开始执行类中定义的 Java 程序代码,为所有类变量进行赋值,执行静态代码块。

3. Java中类加载器

从上面的类加载过程可以看出在初始化阶段以前除了加载阶段,其余阶段都是由虚拟机主导控制。而在加载阶段可以通过自定义类加载器进行参与。类加载器顾名思义是用来加载类的,负责将类的 .class 文件转换成内存中类的二进制字节流。 Java 中的类加载器按类型分有两种:系统类加载器和自定义类加载器。其中系统类加载器有主要有三种,分别是:引导类加载器( Bootstrap ClassLoader )、拓展类加载器( Extensions ClassLoader )和应用程序类加载器( Application ClassLoader )。

3.1 系统类加载器

3.1.1 引导类加载器( Bootstrap ClassLoader

这个类加载器是用 C/C++ 语言实现的,用来加载 JDK 中的核心类,主要加载 $JAVA_HOME/jre/lib 目录下的类,例如 rt.jarresources.jar 等包中的类。

3.1.2 拓展类加载器( Extensions ClassLoader

这个类加载器是用 Java 语言实现的,实现类为 ExtClassLoader ,用来加载 Java 的拓展类,主要加载 $JAVA_HOME/jre/lib/ext 目录和系统属性 java.ext.dir 所指定的目录。

3.1.3 应用程序类加载器( Application ClassLoader

这个类加载器是用 Java 语言实现的,实现类为 AppClassLoader ,可以通过 ClassLoader.getSystemClassLoader 方法获取到,主要加载 Classpath 目录和系统属性 java.class.path 指定的目录下的类。

3.1.4 ClassLoader的继承关系

public class ClassLoaderDemo {
    public static void main(String[] args) {
        ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
        while (classLoader != null) {
            System.out.println("ClassLoader:" + classLoader);
            classLoader = classLoader.getParent();
        }
    }
}
复制代码

这里新建一个 JavaClassLoaderDemo ,循环打印其类加载器和父类加载器,控制台输出结果如下。

Android进阶知识:类加载相关

从结果可以看出 ClassLoaderDemo 类的类加载器是 AppClassLoaderAppClassLoader 的父类加载器是 ExtClassLoader ,而 ExtClassLoader 的父类加载器就为 null 了,这是因为 ExtClassLoader 的父类加载器 BootstrapClassLoader 是由 C/C++ 语言实现的,所以在 Java 中无法获取到它的引用。接下来再进入源码来看一下,先看 AppClassLoader

static class AppClassLoader extends URLClassLoader {
	......
}
复制代码

查看源码发现 AppClassLoader 的父类并不是 ExtClassLoader 而是 URLClassLoader ,再看 ExtClassLoader

static class ExtClassLoader extends URLClassLoader {
    ......
}
复制代码

ExtClassLoader 的父类也是 URLClassLoader 进而再看 URLClassLoader

public class URLClassLoader extends SecureClassLoader implements Closeable {
	......
}

public class SecureClassLoader extends ClassLoader {
	......
}

public abstract class ClassLoader {
	......
}
复制代码

URLClassLoader 的父类是 SecureClassLoader ,而 SecureClassLoader 的父类是 ClassLoaderClassLoader 是一个抽象类。通过对源码的跟踪发现,似乎这里的继承关系与控制台输出的结果不太一致。于是进一步去看输出 ClassLoaderDemo 中调用的 ClassLoader.getParent() 方法源码。

public final ClassLoader getParent() {
        if (parent == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(parent, Reflection.getCallerClass());
        }
        return parent;
    }
     // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;
复制代码

ClassLoadergetParent 方法中看到返回的是一个成员变量中的 parent ,他是一个 ClassLoader 类型对象。继续跟踪寻找它是在哪里初始化赋值的。

private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            domains =
                Collections.synchronizedSet(new HashSet<ProtectionDomain>());
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            domains = new HashSet<>();
            assertionLock = this;
        }
    }

    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
复制代码

跟踪查看源码发现父类加载器 parent 是在 ClassLoader 的构造函数时传入的,如果没有传入默认调用 getSystemClassLoader 方法获取一个父类加载器。接下来继续查看 getSystemClassLoader 方法。

public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
复制代码

getSystemClassLoader 方法中又调用了 initSystemClassLoader 方法初始化系统类加载器,方法最后将这个类加载器 scl 返回。继续查看 initSystemClassLoader 方法。

private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                scl = l.getClassLoader();
      .......
}
复制代码

这个方法中获取到 Launcher 之后调用了 LaunchergetClassLoader 方法获取到创建的类加载器,于是再到 Launcher 中查看。

public ClassLoader getClassLoader() {
        return this.loader;
}

复制代码

LaunchergetClassLoader 方法中返回了其成员变量中的 loader 对象,于是再去寻找这个对象的创建。

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
	......
}

复制代码

Launcher 的构造函数里找到,这里是通过 Launcher.AppClassLoader.getAppClassLoader(var1) 方法创建的 loader ,于是再进入查看。

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
}

复制代码

getAppClassLoader 方法中最终 new 了一个 AppClassLoader 返回,这就又回到了 AppClassLoader 的构造方法。之前看过构造传入的第二个参数就是 parent 父类加载器,这里传入的 var0 可以看到就是 ExtClassLoader 类型。

总结来说 ClassLoader 的父子关系并不是由继承实现的, AB 的父类加载器,并不表示 B 继承了 A ,每个 CLassLoader 中保存了一个它的父类加载器的引用,通过 getParent 方法获得的就是它的值,它是在类加载器创建时构造函数中进行赋值的。如果构造中没有传入父类加载器默认调用 getSystemClassLoader 方法获取系统类加载器,通过查看发现默认系统类加载器就是 AppClassLoader 。在 Launcher 的构造方法中,会依次创建 ExtClassLoaderAppClassLoader ,此时 ExtClassLoader 作为父类加载器由构造函数传入 AppClassLoader 。实际的类加载继承关系如下图。

Android进阶知识:类加载相关
Java类加载器

3.2 自定义类加载器

在一些特殊需求场景下可能需要程序员自定义类加载器。例如从网络下载一个加密过的 .class 类文件,此时就需要自定义类加载器,先进行文件解密再进行类加载。下面就来模拟一下这个例子,先定义一个测试类 TestPrint

public class TestPrint {
    public void printString() {
        System.out.println("测试输出字符串");
    }
}
复制代码

使用 javac 命令编译生成 TestPrint.class 文件。

Android进阶知识:类加载相关
再写个加密文件方法,这里加密就用简单使用下 Base64 加密,将编译生成的 .class

文件转成二进制字节流加密后再保存成本地文件。

public class Test {
    public static void main(String[] args) {
        byte[] classBytes = FileIOUtils.readFile2BytesByStream("/Users/sy/Downloads/ClassLoader/TestPrint.class");
        FileIOUtils.writeFileFromBytesByStream("/Users/sy/Downloads/ClassLoader/TestPrint.class",Base64.getEncoder().encode(classBytes));
}
}
复制代码

得到加密后的 TestPrint.class 后接下来编写自定义的类加载器 MyClassLoader

public class MyClassLoader extends ClassLoader {
    private String path;
    protected MyClassLoader(String path) {
        this.path = path;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class aClass = null;
        // 获取二进制字节流
        byte[] classBytes = loadClassBytes(name);
        if (classBytes == null) {
            System.out.println("class data is null");
        } else {
            aClass = defineClass(name, classBytes, 0, classBytes.length);
        }
        return aClass;
    }
    private byte[] loadClassBytes(String name) {
        String fileName = getFileName(name);
        File file = new File(path, fileName);
        InputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(file);
            outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;

            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            return outputStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    private String getFileName(String name) {
        String fileName;
        int index = name.lastIndexOf(".");
        if (index == -1) {
            fileName = name + ".class";
        } else {
            fileName = name.substring(index + 1) + ".class";
        }
        return fileName;
    }
}


复制代码

自定义类加载器分为以下几个步骤:

  1. 继承抽象类 ClassLoader
  2. 复写 findClass 方法。
  3. findClass 方法中获取到二进制字节流后,调用 defineClass 方法。

MyClassLoaderfindClass 方法中首先读取到本地硬盘下的 TestPrint.class 文件的字节流,然后调用 defineClass 方法,该方法会将字节流转化为 Class 类型。最后写一个测试类调用。

public class TestMyClassLoader {
    public static void main(String[] args) {
        // 初始化类加载器
        MyClassLoader myClassLoader = new MyClassLoader("/Users/sy/Downloads/ClassLoader");
        try {
            // 使用自定义类加载器获取Class
            Class<?> printTest = myClassLoader.loadClass("TestPrint");
            // 创建实例
            Object instance = printTest.newInstance();
            System.out.println("classloader:" + instance.getClass().getClassLoader());
            Method method = printTest.getDeclaredMethod("printString", null);
            method.setAccessible(true);
            method.invoke(instance, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

复制代码

运行结果:

Android进阶知识:类加载相关
此时发生错误是因为做了 Base64 加密,需要在 defineClass 前进行一个解码操作,修改 findClass

方法。

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class aClass = null;
        // 获取二进制字节流
        byte[] classBytes = loadClassBytes(name);
        // Base64 decode
        classBytes = Base64.getDecoder().decode(classBytes);
        if (classBytes == null) {
            System.out.println("class data is null");
        } else {
            aClass = defineClass(name, classBytes, 0, classBytes.length);
        }
        return aClass;
}

复制代码

再次运行查看结果:

Android进阶知识:类加载相关

此时程序就能正常的执行,调用类中的方法了。

4. 双亲委托

4.1 双亲委托模式

学习类加载器就避免不了要了解双亲委托,它是类加载器寻找加载类的模式。还是先看到之前自定义类加载器的例子,自定义时复写了 findClass 方法,但是使用时却没有直接调用这个方法,使用时是通过 ClassLoader.loadClass 方法获得 Class 的。双亲委托模式就是在这个方法中实现的,于是进入查看源码。

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查该类是否加载过
            Class<?> c = findLoadedClass(name);
            // c为空说明没有加载过
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	// 判断是否有父类加载器
                    if (parent != null) {
                      	// 有父类加载器就调用父类加载器的loadClass方法
                        c = parent.loadClass(name, false);
                    } else {
                    	// 没有父类加载器就调用这个方法
                    	// 方法中会调用native方法findBootstrapClass使用BootstrapClassLoader检查该类是否已加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
			   // 走到此处c还为null说明父类加载器没有加载该类
                if (c == null) {
                    long t1 = System.nanoTime();
                    // 就调用自身的findClass查找加载该类
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
}

复制代码

loadClass 中的代码逻辑非常清晰的描述了双亲委托模式,首先通过 findLoadedClass 方法检验要加载的类是否被加载过。加载过直接返回该类的 Class 对象,没加载过则 c 为空进入下面的判断,判断父类加载器 parent 是否为空,不为空调用父类的 loadClass 方法,为空则调用 findBootstrapClassOrNull 方法,该方法中会继续调用 native 方法 findBootstrapClass 使用 BootstrapClassLoader 检查该类是否已加载。接着如果没有加载该类就会调用自身的 findClass 方法查找加载该类。

最后对双亲委托模式做个总结:双亲委托模式是指类加载器加载一个类首先是判断这个类是否加载过,没加载过不会自己直接加载,而是委托给其父类加载器去查找,一直委托到顶层引导类加载器 BootstrapClassLoader ,如果 BootstrapClassLoader 找到该 Class 就会直接返回,没找到就会交给子类加载器依次向下查找,一直没找到最后就会交给自身去查找。

Android进阶知识:类加载相关
双亲委托模式

4.2 双亲委托模式的好处

双亲委托模式有两个好处:

  1. 避免了类的重复加载,一个类如果加载过一次就不需要再次加载了,而是直接读取已经加载的 Class
  2. 类随着它的类加载器一起具备了一种带优先级的层次关系,使得更加安全。例如加载 java.lang.Object 类无论使用哪个类加载器加载,最终都会委托给顶层 BootstrapClassLoader 来加载,这样保证了 Object 类永远是同一个类,不会出现多个不同的 Object 类。这样也无法通过自定义一个 Object 类替换系统原来的 Object 类。

另外 Java 虚拟机判断两个类是同一个类,是依据两个类类名一致,并且被同一个类加载器加载。这里可以在之前的自定义类加载器的基础上测试下。将原来的 MyClassLoader 复制后重命名 YourClassLoader 一份,再编写一个测试类。

public class TestDifferentClassLoader {
    public static void main(String[] args) {
        MyClassLoader myClassLoader = new MyClassLoader("/Users/sy/Downloads/ClassLoader/");
        YourClassLoader yourClassLoader = new YourClassLoader("/Users/sy/Downloads/ClassLoader/");
        try {
            Class<?> myPrintTest = myClassLoader.loadClass("TestPrint");
            // 使用不同的类加载器
            Class<?> yourPrintTest = yourClassLoader.loadClass("TestPrint");

            Object myInstance = myPrintTest.newInstance();
            Object yourInstance = yourPrintTest.newInstance();

            System.out.println(myInstance.getClass().equals(yourInstance.getClass()));
            System.out.println(myInstance.getClass().getName());
            System.out.println(yourInstance.getClass().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

复制代码

运行结果:

Android进阶知识:类加载相关

修改一下用同一个类加载器。

public class TestDifferentClassLoader {
    public static void main(String[] args) {
        MyClassLoader myClassLoader = new MyClassLoader("D://");
        YourClassLoader yourClassLoader = new YourClassLoader("D://");
        try {
            Class<?> myPrintTest = myClassLoader.loadClass("TestPrint");
            // 用同一个类加载器
            Class<?> yourPrintTest = myClassLoader.loadClass("TestPrint");

            Object myInstance = myPrintTest.newInstance();
            Object yourInstance = yourPrintTest.newInstance();

            System.out.println(myInstance.getClass().equals(yourInstance.getClass()));
            System.out.println(myInstance.getClass().getName());
            System.out.println(yourInstance.getClass().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码

运行结果:

Android进阶知识:类加载相关

5. Android中类加载器

Android 中类加载器与 Java 中的类加载器类似但是并不完全相同。 Java 中类加载器是加载 .class 文件,而 Android 中是加载的 dex 文件,不过 Android 中也分系统类加载器和自定义类加载器,系统类加载器主要包括以下三种: BootClassLoaderDexClassLoaderPathClassLoader 。这里还是先循环打印一下 Android 中的父类加载器。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ClassLoader classLoader = MainActivity.class.getClassLoader();
        while (classLoader != null) {
            Log.d("classLoader", "name:" + classLoader);
            classLoader = classLoader.getParent();
        }
    }
}

复制代码

运行日志结果:

Android进阶知识:类加载相关

从日志可以看到这里有两个类加载器,一个是 BootClassLoader 另一个是 PathClassLoaderAndroid 中的每个类加载器里同样保存一个父类加载器的引用, getParent 方法获取到的同样是这个 ClassLoader ,还是先来看一下 Android 中的 ClassLoader 类。

public abstract class ClassLoader {
    private final ClassLoader parent;
		......
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
		
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // 检查该类是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        // 父类加载器不为空则调用其的loadClass方法
                        c = parent.loadClass(name, false);
                    } else {
                    	// 父类加载器为空就调用findBootstrapClassOrNull方法
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 父类加载器没有查找到该类,则调用自身findClass方法查找加载
                    c = findClass(name);
                }
            }
            return c;
    }
    
    private Class<?> findBootstrapClassOrNull(String name)
    {
        return null;
    }
    public final ClassLoader getParent() {
        return parent;
    }
    ......
}
复制代码

Android 中的 ClassLoader 类同样是一个抽象类,它的 loadClass 方法中的逻辑和 Java 中的类似,同样是遵循了双亲委托模式,父类加载器不为空则调用父类加载器的 loadClass 方法,为空则调用 findBootstrapClassOrNull 方法,该方法这里直接返回的 null 。若父类加载器没有查找到需要加载的类,则调用自身的 findClass 方法。

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
}
复制代码

接着看到 ClassLoader 中的 findClass 里是直接抛出了一个 ClassNotFoundException 异常,说明这个方法需要子类来实现。那么接下来就来看看 Android 中类加载器的实际继承关系。

Android进阶知识:类加载相关

抽象类 ClassLoader 定义了类加载器的主要功能,它的子类如上图。先根据源码梳理下它们之间的关系。

public class BaseDexClassLoader extends ClassLoader {
		......
}
public class BaseDexClassLoader extends ClassLoader {
		......
}
public class DexClassLoader extends BaseDexClassLoader {
		......
}
public class PathClassLoader extends BaseDexClassLoader {
		......
}
public final class InMemoryDexClassLoader extends BaseDexClassLoader {
  	......
}
public class URLClassLoader extends SecureClassLoader implements Closeable {
  	......
}
public final class DelegateLastClassLoader extends PathClassLoader {
  	......
}

复制代码

除了这些子类,抽象类 ClassLoader 中还有一个内部类 BootClassLoader

class BootClassLoader extends ClassLoader {
		......
}

复制代码

综上所述 Android 中类加载器的继承关系如下图。

Android进阶知识:类加载相关
Android中类加载器

接下来简单了解一下这些类加载器。

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

复制代码

BootClassLoaderClassLoader 的内部类继承自 ClassLoader ,并且提供了一个获取单例的 getInstance 方法。与 Java 中不同 BootClassLoader 不是用 C/C++ 实现的是用 Java 实现的, Android 系统启动时会使用 BootClassLoader 来预加载常用类。

5.2 DexClassLoader

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
    }
}

复制代码

DexClassLoader 看名字就知道是用来加载 dex 文件的,它的构造函数中有四个参数:

dex
dex
C/C++

DexClassLoader 继承自 BaseDexClassLoader 其主要的方法都在其父类中实现。

5.3 PathClassLoader

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

复制代码

PathClassLoader 是用来加载系统类和应用程序的类,同样继承自 BaseDexClassLoader 类, 它的构造函数里没有 optimizedDirectory 参数,通常用来加载已经安装的 dex 文件。

5.4 BaseDexClassLoader

BaseDexClassLoaderDexClassLoaderPathClassLoader 的父类。

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
    ......
    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());
        }
    }
		......	
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        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 方法中是通过 pathList.findClass 方法获得的 Class 对象。

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

复制代码

findClass 方法中循环遍历了 dexElements 数组,再通过 Element.findClass 方法来获得 Class 对象, Element 又是 DexPathList 的内部类,进一步找到它的 findClass 方法查看。

public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
}

复制代码

这个方法中又调用了 DexFileloadClassBinaryName 方法。

public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
}

private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
}

复制代码

loadClassBinaryName 方法里继续调用了 defineClass 方法,而 defineClass 方法里最终调用了 defineClassNative 这个 native 方法加载 dex 文件。

Android 系统启动后 init 进程会启动 Zygote 进程, Zygote 进程初始化时会预加载常用类,进而会调用 Class.forName 方法,该方法里会通过 BootClassLoader.getInstance 方法创建 BootClassLoader 。之后 Zygote 进程又会启动 SystemServer 进程,进而又会通过 PathClassLoaderFactory 创建 PathClassLoader 。总而言之, Android 中的这两个类加载器在启动时就进行初始化创建了。

原文  https://juejin.im/post/5d6875d3e51d453b1e478b09
正文到此结束
Loading...