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.jar
resources.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();
        }
    }
}
复制代码

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

Android进阶知识:类加载相关

从结果可以看出 ClassLoaderDemo
类的类加载器是 AppClassLoader
AppClassLoader
的父类加载器是 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
的父类是 ClassLoader
ClassLoader
是一个抽象类。通过对源码的跟踪发现,似乎这里的继承关系与控制台输出的结果不太一致。于是进一步去看输出 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;
复制代码

ClassLoader
getParent
方法中看到返回的是一个成员变量中的 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
之后调用了 Launcher
getClassLoader
方法获取到创建的类加载器,于是再到 Launcher
中查看。

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

复制代码

Launcher
getClassLoader
方法中返回了其成员变量中的 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
的父子关系并不是由继承实现的, A
B
的父类加载器,并不表示 B
继承了 A
,每个 CLassLoader
中保存了一个它的父类加载器的引用,通过 getParent
方法获得的就是它的值,它是在类加载器创建时构造函数中进行赋值的。如果构造中没有传入父类加载器默认调用 getSystemClassLoader
方法获取系统类加载器,通过查看发现默认系统类加载器就是 AppClassLoader
。在 Launcher
的构造方法中,会依次创建 ExtClassLoader
AppClassLoader
,此时 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
    方法。

MyClassLoader
findClass
方法中首先读取到本地硬盘下的 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
中也分系统类加载器和自定义类加载器,系统类加载器主要包括以下三种: BootClassLoader
DexClassLoader
PathClassLoader
。这里还是先循环打印一下 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
另一个是 PathClassLoader
Android
中的每个类加载器里同样保存一个父类加载器的引用, 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;
    }
  	......
}

复制代码

BootClassLoader
ClassLoader
的内部类继承自 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

BaseDexClassLoader
DexClassLoader
PathClassLoader
的父类。

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

复制代码

这个方法中又调用了 DexFile
loadClassBinaryName
方法。

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

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Android进阶知识:类加载相关

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址