Android ClassLoader的前世今生
一直以来我对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的整体架构看起
imageVJM 整体被分为三个子系统:
(1)类加载器子系统 (2)运行时数据区 (3)执行引擎
1.1、 类加载器子系统
imageJava类的动态加载功能是由类加载器子系统处理。当它在运行时(不是编译时)首次引用一个类时,加载、链接并初始化该类文件。
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