热修复和插件化是目前比较热门的技术,要想更好地掌握它们需要先了解ClassLoader。 关于ClassLoader,可能有的同学会认为Java中的ClassLoader和Android中的ClassLoader 没有区别,在第11章中我们知道DVM和ART加载的是dex文件,而JVM加载的是Class 文件,因此它们的类加载器ClassLoader肯定是有区别的。这一章分别介绍Java中的 ClassLoader和Android中的ClassLoader,这样它们的区别也就一目了然了。
在10.2.3节中提到过类加载子系统,它的主要作用就是通过多种类加载器(ClassLoader) 来查找和加载Class文件到Java虚拟机中。
Java中的类加载器主要有两种类型,即系统类加载器和自定义类加载器。其中系统类加载器包括 3 种,分别是 Bootstrap ClassLoader, Extensions ClassLoader 和 Application ClassLoader。
1.Bootstrap ClassLoader (引导类加载器)
C/C++代码实现的加载器,用于加载指定的JDK的核心类库,比如java.lang., java.uti. 等这些系统类。它用来加载以下目录中的类库:
Java虚拟机的启动就是通过Bootstrap ClassLoader创建一个初始类来完成的。由于 Bootstrap ClassLoader是使用C/C++语言实现的,所以该加载器不能被Java代码访问到。 需要注意的是,Bootstrap ClassLoader并不继承java.lang.ClassLoader0我们可以通过如下代 码来得出Bootstrap ClassLoader所加载的目录:
public class ClassLoaderTest {
public static void main(String[]args) {
System.out.println(System.getProperty("sun.boot.class.path"));
}
}
打印结果为:
C:\Program Files\Java\jdkl.8.0_102\j re\lib\resources.jar;C:\Program Files'Java' jdk1.8.0_102\jre\lib\rt.jar; C:\Program Files\Java\jdkl.8.0_102\jre\lib\sunrsasign.jar; C:\Program Files\Java\jdkl.8.0_102\jre\lib\jsse.jar;C:\Program Files\Java\jdkl.8.0_102\ jre\lib\jce.jar; C:\Program Files\Java\jdkl.8.0_102\j re\lib\charsets.jar; C:\Program Files'Java'jdkl.8.0_102\jre\lib\jfr.jar;C:\Program Files\Java\jdkl.8.0_102\jre\classes
可以发现几乎都是$JAVA_HOME/jre/lib目录中的jar包,包括rt.jar、resources.jar和 charsets.jar 等。
2.Extensions ClassLoader (拓展类加载器)
Java中的实现类为ExtClassLoader,因此可以简称为ExtClassLoader,它用于加载Java 的拓展类,提供除了系统类之外的额外功能。ExtClassLoader用来加载以下目录中的类库:
通过以下代码可以得到Extensions ClassLoader加载目录:
System.out.printin(System.getProperty("java.ext.dirs“));
打印结果为:
C:\Program Files\Java\jdkl.8.0_102\jre\lib\ext; C:\Windows\Sun\Java\lib\ext
3.Application ClassLoader (应用程序类加载器)
Java中的实现类为AppClassLoader,因此可以简称为AppClassLoader,同时它又可以称作System ClassLoader (系统类加载器),这是因为AppClassLoader可以通过ClassLoader 的getSystemClassLoader方法获取到。它用来加载以下目录中的类库:
4.Custom ClassLoader (自定义类加载器)
除了系统提供的类加载器,还可以自定义类加载器,自定义类加载器通过继承 java.lang.ClassLoader 类的方式来实现自己的类加载器,Extensions ClassLoader 和 App ClassLoader也继承了 java.lang.ClassLoader类。关于自定义类加载器后面会进行介绍。
运行一个Java程序需要用到几种类型的类加载器呢?如下所示。
public class ClassLoaderTest (
public static void main(String[] args) {
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while (loader != null) {
System.out.printin(loader);//1
loader = loader.getParent();
}
}
}
我们得到当前类ClassLoaderTest的类加载器,并在注释1处打印出来,紧接着打印出当前类的类加载器的父加载器,直到没有父加载器时就终止循环,打印结果如下所示:
sun.misc.Launcher$AppClassLoader@75b84c92
sun.misc.Launcher$ExtClassLoader@lb6d3586
第1行说明加载ClassLoaderTest的类加载器是AppClassLoader,第2行说明 AppClassLoader的父加载器为ExtClassLoader。至于为何没有打印出ExtClassLoader的父加载器 Bootstrap ClassLoader,这是因为 Bootstrap ClassLoader 是由 C/C++编写的,并不是一 个Java类,因此我们无法在Java代码中获取它的引用。系统所提供的类加载器有3种类型, 但是系统提供的ClassLoader却不只有3个。另外,AppClassLoader的父类加载器为ExtClassLoader,并不代表 AppClassLoader 继承自 ExtClassLoader, ClassLoader 的继承关系如图12.1所示。
可以看到图12-1中共有5个ClassLoader相关类,下面简单对它们进行介绍。
类加载器查找Class所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该Class是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行査找,这样依次进行递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去査找。这样讲可能会有些抽象,来看图12-2。
图12-2双亲委托模式
我们知道类加载子系统用来查找和加载Class文件到Java虚拟机中,假设我们要加载一个位于D盘的Class文件,这时系统所提供的类加载器不能满足条件,这时就需要我们自定义类加载器继承自java.lang.ClassLoader,并复写它的findClass方法。加载D盘的Class 文件步骤如下:
总的来说就是Class文件加载到类加载子系统后,先沿着图12-2中虚线的方向自下而上进行委托,再沿着实线的方向自上而下进行查找和加载,整个过程就是先上后下。结合 12.1.2节中讲的ClassLoader的继承关系,可以得出ClassLoader的父子关系并不是使用继 承来实现的,而是使用组合来实现代码复用的。
类加载的步骤在JDK8的源码中也得到了体现,下面来査看抽象类ClassLoader的 loadClass 方法:
protected Class> More ...loadClass(String name, boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class> c = findLoadedClass(name);//l
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(namez false);//2
} else {
c = findBootstrapClassOrNull(name);//3
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long tl = System.nanoTime();
c = findClass(name);//4
sun.misc.PerfCounter.getParentDelegationTime().addTime(tl - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(tl);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveclass(c);
}
return c;
}
}
在注释1处用来检査传入的类是否已经加载,如果已经加载则后面的代码不会执行, 最后会返回该加载类。没有加载会接着向下执行,在注释2处,如果父类加载器不为null, 就调用父类加载器的loadClass方法。如果父类加载器为null就调用注释3处的 findBootstrapClassOrNull 方法,这个方法内部调用了 Native 方法 findBootstrapClass, findBootstrapClass方法中最终会用Bootstrap Classloader来检査该类是否已经加载,如果没有加载就说明向上委托流程中没有加载该类,则调用注释4处的findClass方法继续向下进行査找流程。
采取双亲委托模式主要有如下两点好处。
系统提供的类加载器只能够加载指定目录下的jar包和Class文件,如果想要加载网络 上的或者D盘某一文件中的jar包和Class文件则需要自定义ClassLoader。实现自定义 ClassLoader需要如下两个步骤:
下面我们就自定义一个ClassLoader用来加载位于D:\lib的Class文件。首先编写测试类并生成Class文件,如下所示:
package com.example;
public class Jobs {
public void say () {
System.out.printin("One more thing");
}
}
将这个Jobs.java放入到D:\lib中,使用cmd命令进入D:\lib目录中,执行Javac Jobs.java 对该java文件进行编译,这时会在D:\lib中生成Jobs.class。接下来在AS中创建一个Java Library,编写自定义ClassLoader,如下所示:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.Fileinputstream;
import java.io.lOException;
import java.io.InputStream;
class DiskClassLoader extends ClassLoader {
private String path;
public DiskClassLoader(String path) {
this.path = path;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
byte[] classData = loadClassData(name);//l
if (classData == null) {
throw new ClassNotFoundException();
} else {
clazz= defineClass(name, classData, 0, classData.length);//2
}
return clazz;
}
private byte[] loadClassData(String name) {
String fileName = getFileName(name);
File file = new File(path,fileName);
Inputstream in=null;
ByteArrayOutputStream out=null;
try {
in = new FilelnputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length=0;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
return out.toByteArray();
} catch (lOException e) {
e.printStackTrace();
}finally {
try {
if(in!=null) {
in.close();
}
} catch (IOException e) (
e.printStackTrace ();
}
try{
if(out!=null) {
out.close();
}
}catch (IOException e)(
e.printStackTrace();
}
}
return null;
}
private String getFileName(String name) {
int index = name.lastlndexOf(*.*);
if (index == -1) {
//如果没有找到,.,则直接在末尾添加.class
return name+,*.class*';
}else{
return name.substring(index+l)+n.class";
}
}
}
这段代码有几点需要注意的,注释1处的loadClassData方法会获得class文件的字节码数组,并在注释2处调用defineClass方法将class文件的字节码数组转为Class类的实例。 在loadClassData方法中需要对流进行操作,关闭流的操作要放在finally语句块中,并且要 对in和out分别使用try语句,如果in和out共同在一个try语句中,假设inclose()发生异常 的话,就无法执行out.close()。最后我们来验证DiskClassLoader是否可用,代码如下所示:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ClassLoaderTest {
public static void main(String!] args) {
DiskClassLoader diskClassLoader = new DiskClassLoader("D:\\lib");//l
try {
Class c = diskClassLoader.loadClass("com.example.Jobs");//2
if (c != null) {
try {
Object obj = c.newlnstance();
System.out.println(obj.getClass().getClassLoader());
Method method = c.getDeclaredMethod("say", null);
method.invoke(obj, null);//3
} catch (InstantiationException | IllegalAccessException
| NoSuchMethodException
| SecurityException
| IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在注释1处创建DiskClassLoader并传入要加载类的路径,在注释2处加载Class文件, 需要注意的是,不要在项目工程中存在名为com.example.Jobs的Java文件,否则就不会使 用DiskClassLoader来加载,而是使用AppClassLoader来负责加载,这样我们定义的 DiskClassLoader就变得毫无意义。接下来在注释3处通过反射来调用Jobs的say方法,打印结果如下:
com.example.DiskClassLoader@4554617c One more thing
使用了 DiskClassLoader来加载Class文件,say方法也正确执行,我们的目的就达到了。
在12.1节我们学习了 Java中的ClassLoader,有的读者会把Android和Java中的 ClassLoader 搞混,甚至会认为 Java 中的 ClassLoader 和 Android 中的 ClassLoader 是一样的, 这显然是不对的。这一篇文章我们就来学习Android中的ClassLoader,来查看它和Java中 的ClassLoader有何不同。
我们知道Java中的ClassLoader可以加载jar文件和Class文件(本质是加载Class文 件),这一点在Android中并不适用,因为无论是DVM还是ART,它们加载的不再是Class 文件,而是dex文件,这就需要重新设计ClassLoader相关类,我们先来学习ClassLoader 的类型。
Android中的ClassLoader类型和Java中的ClassLoader类型类似,也分为两种类型, 分别是系统类加载器和自定义加载器。其中系统类加载器主要包括3种,分别是 BootClassLoader, PathClassLoader 和 DexClassLoader。
1.BootClassLoader
Android系统启动时会使用BootClassLoader来预加载常用类,与SDK中的Bootstrap ClassLoader不同,它并不是由C/C++代码实现的,而是由Java实现的,BootClassLoader 的代码如下所示:
libcore/ojluni/src/main/java/java/lang/ClassLoader.java
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings ("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getlnstance() {
if (instance == null) (
instance = new BootClassLoader();
}
return instance;
}
}
BootClassLoader 是 ClassLoader 的内部类,并继承自 ClassLoader。BootClassLoader 是 一个单例类,需要注意的是BootClassLoader的访问修饰符是默认的,只有在同一个包中才 可以访问,因此我们在应用程序中是无法直接调用的。
2.DexClassLoader
DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),不管加载哪种文件,最终都要加载dex文件,在这一章为了方便理解和叙述,将dex文件以及包 含dex的压缩文件统称为dex相关文件。查看DexClassLoader的代码,如下所示:
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader (
public DexClassLoader(String dexPath, String optimizedDirectory,String 1ibrarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), 1ibrarySearchPath, parent);
}
}
DexClassLoader的构造方法有如下4个参数。
DexClassLoader 继承自 BaseDexClassLoader,方法都在 BaseDexClassLoader 中实现。
3.PathClassLoader
Android系统使用PathClassLoader来加载系统类和应用程序的类,下面来查看它的代码:
libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
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,也都在 BaseDexClassLoader 中实现。
在 PathClassLoader 的构造方法中没有参数 optimizedDirectory,这是因为 PathClassLoader 已经默认了参数 optimizedDirectory 的值为/data/dalvik-cache,很显然 PathClassLoader 无法 定义解压的dex文件存储路径,因此PathClassLoader通常用来加载已经安装的apk的dex 文件(安装的apk的dex文件会存储在/data/dalvik-cache中)。
运行一个应用程序需要用到几种类型的类加载器呢?如下所示:
public class MainActivity extends AppCompatActivity (
@Override
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.layout.activity_main);
ClassLoader loader = MainActivity.class.getClassLoader();
while (loader != null) (
Log.d("liuwangshu", loader.toString()) ;//l
loader = loader.getParent();
}
}
}
首先我们得到MaiiiActivity的类加载器,并在注释1处通过Log打印出来,接着通 过循环打印出当前类的类加载器的父加载器,直到没有父加载器终止循环,打印结果如下所示:
可以看到有两种类加载器,一种是PathClassLoader,另一种则是BootClassLoader。 DexPathList 中包含了 很多 apk 的路径,其中 /data/app/com.example.liuwangshu.moonclassloader-2/base.apk就是示例应用安装在手机上的位置。DexPathList是在 BaseDexClassLoader的构造方法中创建的,里面存储了 dex相关文件的路径,在 ClassLoader执行双亲委托模式的査找流程时会从DexPathList中进行查找,在12.2.3节我们还会再提到它。
除了上面所讲的3种主要的类加载器外,Android还提供了其他的类加载器和
ClassLoader相关类,ClassLoader的继承关系如图12-3所示。
图 12-3 Android8.0 中 ClassLoader 的继承关系
可以看到图12-3中一共有8个ClassLoader相关类,其中有一些和Java中的ClassLoader 相关类十分类似,下面简单对它们进行介绍:
Android的ClassLoader同样遵循了双亲委托模式,ClassLoader的加载方法为loadClass 方法,这个方法被定义在抽象类ClassLoader中,如下所示:
libcore/ojluni/src/main/java/java/lang/ClassLoader.java
protected Class> loadclass(String name, boolean resolve) throws ClassNotFoundException{
Class> c = findLoadedClass(name);//I
if (c = null) {
try {
if (parent != null) {//2
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);//3
}
} catch (ClassNotFoundException e) {
}
if (c == null) {//4
c = findClass(name);//5
}
return c;
}
ClassLoader 的 loadClass 方法和 12.1.3 节讲的 loadClass 方法(JDK 中 ClassLoader 的 loadClass方法)类似。在注释1处用来检查传入的类是否已经加载,如果已经加载就返回该类,如果没有加载就在注释2处判断父加载器是否存在,存在就调用父加载器的loadClass 方法,如果不存在就调用注释3处的findBootstrapClassOrNull方法,这个方法会直接返回 nullo如果注释4处的代码成立,说明向上委托流程没有检査出类已经被加载,就会执行注 释5处的findClass方法来进行查找流程,findClass方法如下所示:
libcore/ojluni/src/main/java/java/lang/ClassLoader.java
protected Class> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
在findClass方法中直接抛出了异常,这说明findClass方法需要子类来实现, BaseDexClassLoader的代码如下所示:
libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
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 suppressedExceptions = new ArrayList();
Class c = pathList.findClass(namez suppressedExceptions);//l
if (c = null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \n** + name + ”on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
在BaseDexClassLoader的构造方法中创建了 DexPathList,在注释1处调用了 DexPathList 的 findClass 方法:
libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public Class> findClass(String name, List suppressed) {
for (Element element : dexElements) (//1
Class> clazz = element. findClass (name, definingContext, suppressed) ; //2
if (clazz != null) {
return clazz;
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
在注释1处遍历Element数组dexElements,在注释2处调用Element的findClass方法, Element是DexPathList的静态内部类:
libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
static class Element (
private final File path;
private final DexFile dexFile;
private ClassPathURLStreamHandler urlHandler;
private boolean initialized;
public Element(DexFile dexFile, File dexZipPath) {
this.dexFile = dexFile;
this.path = dexZipPath;
}
public Element(DexFile dexFile) {
this.dexFile = dexFile;
this.path = null;
}
public Element(File path) {
this.path = path;
this.dexFile = null;
}
public Class> findClass(String name, ClassLoader definingContext, List suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(namez defining Context, suppressed) : null;//1
}
}
从Element的构造方法可以看出,其内部封装了 DexFile,它用于加载dex。在注释1 处如果 DexFile 不为 null 就调用 DexFile 的 loadClassBinaryName 方法:
libcore/dalvik/src/main/java/dalvik/system/DexFile.java
public Class loadClassBinaryName (String name, ClassLoader loader, List suppressed) (
return defineClass(name, loader, mCookie, this, suppressed);
}
在 loadClassBinaryName 方法中调用了 defineClass 方法:
libcore/dalvik/src/main/java/dalvik/system/DexFile.java
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List supp ressed) {
Class result = null;
try {
result = defineClassNative(name, loader, cookie, dexFile);//1
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
在注释1处调用了 defineClassNative方法来加载dex相关文件,这个方法是Native方 法,这里就不再进行分析,有兴趣的读者可以自行阅读源码。ClassLoader的加载过程就是 遵循着双亲委托模式,如果委托流程没有检查到此前加载过传入的类,就调用ClassLoader 的findClass方法,Java层最终会调用DexFile的defineClassNative方法来执行查找流程, 如图12.4所示。
图12-4 ClassLoader查找流程
BootClassLoader是在何时被创建的呢?这得先从Zygote进程开始说起,Zygotelnit的 main方法如下所示:
frameworks/base/core/iava/com/android/internal/os/Zygotelnit.java
public static void main(String argv[]) {
try {
preload(bootTimingsTraceLog);
}
}
main方法是Zygotelnit的入口方法,其中调用了 Zygotelnit的preload方法,在preload 方法中又调用了 Zygotelnit的preloadClasses方法,如下所示:
frameworks/base/core/java/com/android/intemal/os/Zygotelnit.java
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes”;
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
//将/system/etc/preloaded-classes 文件封装成 FilelnputStream
is = new FilelnputStream(PRELOADED_CLASSES);//l
} catch (FileNotFoundException e) {
Log.e (TAG, "Could't find " + PRELOADED_CLASSES + ".");
return;
}
try {
//将 FilelnputStream 封装为 BufferedReader
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 {
}
}
preloadClasses方法用于Zygote进程初始化时预加载常用类。在注释1处 PRELOADED CLASSES 的值为将/system/etc/preloaded-classes 文件封装成 FilelnputStream, preloaded-classes文件中存有预加载类的目录,这个文件在系统源码中的路径为 frameworks/base/preloaded-classes,这里列举一些 preloaded-classes 文件中的预加载类名称, 如下所示:
android.app.ApplicationLoaders
android.app.ApplicationPackageManager
android.app.ApplicationPackageManager$OnPennissionsChangeListenerDelegate
android.app.ApplicationPackageManager$ResourceName
android.app.ContentProviderHolder
android.app.ContentProviderHolder$1
android.app.Contextlmpl
android.app.ContextImpl$ApplicationContentResolver
android.app.DexLoadReporter
android.app.Dialog
android.app.Dialog$ListenersHandler
android.app.DownloadManager
android.app.Fragment
可以看到preloaded-classes文件中的预加载类的名称有很多都是我们非常熟知的。预加 载属于拿空间换时间的策略,Zygote环境配置得越健全越通用,应用程序进程需要单独做 的事情也就越少,预加载除了预加载类,还有预加载资源和预加载共享库,因为不是本节 的重点,这里就不再延伸讲下去了。回到preloadClasses方法的注释2处,将FilelnputStream 封装为BufferedReader,并在注释3处遍历BufferedReader,读出所有预加载类的名称,每 读出一个预加载类的名称就调用注释4处的代码加载该类,Class的forName方法如下所示:
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.getlnstance();//l
}
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;
}
在注释1处创建了 BootClassLoader,并将BootClassLoader实例传入到了注释2处的 classForName方法中,classForName方法是Native方法,它的实现由C/C++代码来完成, 如下所示:
@FastNative
static native Class> classForName(String className, boolean shouldlnitialize, ClassLoader ClassLoader) throws ClassNotFoundException;
Native方法这里就不再分析了,我们知道了 BootClassLoader是在Zygote进程的Zygote 入口方法中被创建的,用于加载preloaded-classes文件中存有的预加载类。
PathClassLoader的创建也得从Zygote进程开始说起,Zygote进程启动SystemServer 进程时会调用Zygotelnit的startSystemServer方法,如下所示:
frameworks/base/core/java/com/android/internal/os/Zygotelnit.java
private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller, RuntimeException {
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/*1*/
pid = Zygote.forkSystemServer(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,null,parsedArgs.permittedCapabilities,parsedArgs.effectivecapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
if (pid == 0) {//2
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);//3
}
return true;
}
在注释1处,Zygote进程通过forkSystemServer方法fork自身创建子进程(SystemServer 进程)。在注释2处如果forkSystemServer方法返回的pid等于0,说明当前代码是在新创 建的SystemServer进程中执行的,接着就会执行注释3处的handleSystemServerProcess方法:
frameworks/base/core/java/com/android/internal/os/Zygotelnit.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);//l
Thread.currentThread().setContextClassLoader(cl);
}
Zygotelnit.zygotelnit(parsedArgs.targetSdkVersion, parsedArgs. remainingArgs, cl);
}
}
在注释1处调用了 createPathClassLoader方法,如下所示:
frameworks/base/core/java/com/android/intemal/os/Zygotelnit.java
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 */);
}
在 createPathClassLoader 方法中又调用了 PathClassLoaderFactory 的 createClassLoader 方法,看来PathClassLoader是用工厂来进行创建的:
frameworks/base/core/java/com/android/internal/os/PathClassLoaderFactoryjava
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;
}
在 PathClassLoaderFactory 的 createClassLoader 方法中会创建 PathClassLoader.讲到这里可以得出结论,PathClassLoader是在SystemServer进程中采用工厂模式创建的。
本章分别对Java和Android的ClassLoader进行解析,通过Java和Android的ClassLoader 的类型和ClassLoader的继承关系,就可以很清楚地看出它们的差异,主要有以下几点: