文章目录
概述
本篇为讲述 android中的 虚拟机
前面是铺垫咯
引入:
人人都知道 android 的代码是用java写的, 以为会做后台开发的就会做android开发了(事实就是这样,只是调用的各种类库,框架不一样而已), 所以 有些面试公司面试时,如果没有android面试官,就会让后台java程序员来做 android 的面试官.
当问到虚拟机这块时,
虚拟机中的顶级类加载器是哪个?
继承关系是什么样的?
后台答案:
1 Bootstrap ClassLoader
2 AppClassLoader/ExtClassLoader-->URLClassLoader-->SecureClassLoader-->Bootstrap ClassLoader
android答案:
1 BootClassLoader
2 PathClassLoader/DexClassLoader-->BaseDexClassLoader-->BootClassLoader
相信一开始面试官是懵逼的,心中一定会想,这怎么和我知道的不一样,这家伙也不像乱说呀. 面试过后,百度一下就会发现,
java后台与android 使用的虚拟机 两者 大相径庭.
java与android 二者使用的虚拟机不同
先看表,有一个大体认识
对比项目 | java | android4.4之前 | android5.0之后 |
---|---|---|---|
虚拟机 | jvm | DVM | ART |
可解析文件格式 | .class | .dex/.odex | .dex/.odex |
classloader的关系 | 体系不同 | 与jvm体系不同 | 与jvm体系不同 |
classloader种类 | 系统ClassLoader和自定义classloader | 系统ClassLoader和自定义classloader | 系统ClassLoader和自定义classloader |
运行架构 | 基于栈运行 | 基于寄存器运行 | 基于寄存器运行 |
编译方式 | JIT即时编译技术 | JIT即时编译技术 | /4.4部分,5.0后所有 AOT 预编译技术 |
单次指令占用空间 | 1字节 | 2字节(携带的信息比1字节多效率高) | 2字节(携带的信息比1字节多效率高) |
java 与android classloader 对象间的关系
虽然都是系统classloader和自定义classloader两类,但是其中内容和架构都是不同的
正如之前面试场景中的答案那样,在系统classloader中
java的三种为
- Bootstrap ClassLoader(java的顶级 加载器)
- Extensions ClassLoader
- App ClassLoader
android 的为
- BootClassLoader(android的顶级加载器)
- PathClassLoader
- DexClassLoader
二者的顶级加载器所在位置不同
java中的在c层,java层访问不到该对象
android中的在java层,java层可以访问到该对象
android 中的classloader
BootClassLoader
BootClassLoader是ClassLoader的内部类,并继承自ClassLoader。 代码为 andorid 28
所在路径
package java.lang;
static private class SystemClassLoader {
public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
private static ClassLoader createSystemClassLoader() {
String classPath = System.getProperty("java.class.path", ".");
String librarySearchPath = System.getProperty("java.library.path", "");
return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
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;
}
//注意看这里, 构造方法中父类传入的是null,所以 bootclassloader中是没有父类的,它自己就是顶级父类
public BootClassLoader() {
super(null);
}
......
}
BootClassLoader是一个单例类,需要注意的是BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问,因此我们在应用程序中是无法直接调用的。
PathClassLoader
Android系统使用PathClassLoader来加载系统类和应用程序的类,继承自BaseDexClassLoader,实现也都在BaseDexClassLoader中
所在路径package dalvik.system
8.0以上PathClassLoader也可以加载未安装的apk(包含dex的压缩文件)
DexClassLoader
所在路径package dalvik.system
DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),
不管是加载哪种文件,最终都是是加载dex文件,为了方便理解和叙述,将dex文件以及包含dex的压缩文件统称为dex相关文件。
继承自BaseDexClassLoader ,方法实现都在BaseDexClassLoader中。热加载热修复热更新的基础,都是在这个基础上来的
InMemoryDexClassLoader
主要加载内存中的dex文件
andorid 类加载器间的关系
图1
Android中具体负责类加载的并不是哪个ClassLoader,而是通过DexFile的defineClassNative()方法来加载的。
andorid中的类加载机制 也是双亲委派机制
在Android中如果parent类加载器找不到类,最终还是会调用ClassLoader对象自己的findClass()方法。这个与在Java中逻辑是一样的。
ps : 在android studio中 看到的 此部分源码几乎都是throw new RuntimeException("Stub!")
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
我要是个小白 还以为实现就是这样的呢(好吧,我就是小白), 其实是因为 此部分代码无法使用android studio查看,
查看源码方式 为
[源码网站一] (http://androidxref.com)
[源码网站二] (https://www.androidos.net.cn/sourcecode)
或者干脆 你可以将源码下载下来 使用api 文档浏览器 Dash
(mac 电脑)https://kapeli.com/dash
言归正传 在BaseDexClassLoader
中
@Override
130 protected Class<?> findClass(String name) throws ClassNotFoundException {
131 List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
此处为在一个集合中找 需要加载的类
132 Class c = pathList.findClass(name, suppressedExceptions);
133 if (c == null) {
134 ClassNotFoundException cnfe = new ClassNotFoundException(
135 "Didn't find class \"" + name + "\" on path: " + pathList);
136 for (Throwable t : suppressedExceptions) {
137 cnfe.addSuppressed(t);
138 }
139 throw cnfe;
140 }
141 return c;
142 }
DexPathList 源码中 可以看到 在pathList 中找需要加载的类 看看之前是否加载过该类
public Class<?> findClass(String name, List<Throwable> suppressed) {
485 for (Element element : dexElements) {
此处是在找这个类是否存在
486 Class<?> clazz = element.findClass(name, definingContext, suppressed);
487 if (clazz != null) {
488 return clazz;
489 }
490 }
491
492 if (dexElementsSuppressedExceptions != null) {
493 suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
494 }
495 return null;
496 }
element 是DexPathList的一个内部类
public Class<?> findClass(String name, ClassLoader definingContext,
723 List<Throwable> suppressed) {
724 return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
725 : null;
726 }
这个Element数组是DexPathList初始化的时候创建的
这里就有意思了
由于数组是有序的, for循环时,如果找到一个对象,就会返回
那么,如果数组中存在两个同名对象 会如何呢,没错,会先加载 两者中的排前面的一个.有些热加载,热修复,就是这样实现的,在初始化时, 把修复的dex文件放在DexPathList中Element[]数组的前面,这样就实现了修复后的Class抢先加载了,达到了修改bug的目的。
加载class 调用对象到底是谁
Android加载一个Class是调用DexFile的defineClass()方法。而不是调用ClassLoader的defineClass()方法。这一点与Java不同,毕竟Android虚拟机加载的dex文件,而不是class文件。
在Android中ClassLoader的defineClass()方法已经不能用了。可以看到它的方法体里直接抛出异常了,而且在BaseDexClassLoader中也没有重写这个方法,毕竟BaseDexClassLoader加载类的逻辑已经变了。
所以 android中的加载是这样的
ClassLoader.loadClass方法==>BaseDexClassLoader.findClass方法==>DexPathList.findClass方法==>DexFile.loadClassBinaryName方法加载二进制文件
classloader的创建时机
BootClassLoader的创建时机
Zygote进程
PathClassLoader的创建时机
在PathClassLoaderFactory的createClassLoader方法中会创建PathClassLoader
参考
以上源码版本为 android9.0.0_r3
ps: 阅读不同版本的源码 会有所不同,有时,通过对比各个版本的相同功能的实现,可以看到,迭代的实现思路,发现 大神的优化 过程,在自己代码中也可以模仿,不知不觉,自己的 代码风格,和代码格局,也会提高的
这篇帖子 是我整理的知识点,但是里面的知识点,很多前辈都讲过,而我,其实是跟着他们的思路,一点点看源码.有些帖子中的代码,和我看的,是不一样的,就是 源码版本不同的导致的,但这并不影响 理解整体框架. 也感谢各位前辈给指路.
-
https://blog.csdn.net/zc19921215/article/details/83934489
-
https://www.jianshu.com/p/c54285a0095b
-
https://www.jianshu.com/p/7193600024e7
-
https://blog.csdn.net/mingyunxiaohai/article/details/86677509
-
https://blog.csdn.net/c10wtiybq1ye3/article/details/86536027
https://www.jianshu.com/p/0820f3aa9eef ↩︎