class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)var classLoader = this.classLoader// 打印 ClassLoader 继承关系while (classLoader != null) {Log.d("MainActivity", classLoader.toString())classLoader = classLoader.parent}}}将 MainActivity 的类加载器打印出来,并且打印当前类加载器的父加载器,直到没有父加载器,则终止循环 。打印结果如下:com.zhgqthomas.github.hotfixdemo D/MainActivity: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.zhgqthomas.github.hotfixdemo-2/base.apk"],nativeLibraryDirectories=[/data/app/com.zhgqthomas.github.hotfixdemo-2/lib/arm64, /oem/lib64, /system/lib64, /vendor/lib64]]]com.zhgqthomas.github.hotfixdemo D/MainActivity: java.lang.BootClassLoader@4d7e926可以看到有两种类加载器,一种是 PathClassLoader,另一种则是 BootClassLoader。DexPathList 中包含了很多路径,其中 /data/app/com.zhgqthomas.github.hotfixdemo-2/base.apk 就是示例应用安装在手机上的位置 。双亲委托模式类加载器查找 Class 所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该 Class 是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行查找,这样依次的进行递归,直到委托到最顶层的 BootstrapClassLoader,如果 BootstrapClassLoader 找到了该 Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找 。这是 JDK 中 ClassLoader 的实现逻辑,Android 中的 ClassLoader 在 findBootstrapClassOrNull 方法的逻辑处理上存在差异 。
// ClassLoader.javaprotected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();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.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the stats}}return c;}上面的代码很容易理解,首先会查找加载类是否已经被加载了,如果是直接返回,否则委托给父加载器进行查找,直到没有父加载器则会调用 findBootstrapClassOrNull 方法 。下面看一下 findBootstrapClassOrNull 在 JDK 和 Android 中分别是如何实现的
// JDK ClassLoader.javaprivate Class<?> findBootstrapClassOrNull(String name){if (!checkName(name)) return null;return findBootstrapClass(name);}JDK 中 findBootstrapClassOrNull 会最终交由 BootstrapClassLoader 去查找 Class 文件,上面提到过 BootstrapClassLoader 是由 C++ 实现的,所以 findBootstrapClass 是一个 native 的方法// JDK ClassLoader.javaprivate native Class<?> findBootstrapClass(String name);在 Android 中 findBootstrapClassOrNull 的实现跟 JDK 是有差别的// Androidprivate Class<?> findBootstrapClassOrNull(String name){return null;}Android 中因为不需要使用到 BootstrapClassLoader 所以该方法直接返回来 null正是利用类加载器查找 Class 采用的双亲委托模式,所以可以利用反射修改类加载器加载 dex 相关文件的顺序,从而达到热修复的目的
类加载过程通过上面分析可知
- PathClassLoader 可以加载 Android 系统中的 dex 文件
- DexClassLoader 可以加载任意目录的 dex/zip/apk/jar 文件,但是要指定 optimizedDirectory。
BaseDexClassLoader
// libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.javapublic class BaseDexClassLoader extends ClassLoader {...private final DexPathList pathList;public BaseDexClassLoader(String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent) {this(dexPath, optimizedDirectory, librarySearchPath, parent, false);}/*** @hide*/public BaseDexClassLoader(String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent, boolean isTrusted) {super(parent);this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);if (reporter != null) {reportClassLoaderChain();}}...public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {// TODO We should support giving this a library search path maybe.super(parent);this.pathList = new DexPathList(this, dexFiles);}...}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 使用Apache协议的是自由软件吗?
- 昆仑雪菊胎菊冲泡方式,昆仑雪菊茶的冲泡步骤
- 景洪勐宋古茶山,勐宋古茶山滑竹梁子
- 苦丁的功效与作用,发酵小叶苦丁茶的功效与作用
- Python实现数据压缩如此简单
- 禁忌喝的八种茶,喝苦瓜茶的禁忌
- 不要忽视 .gitignore
- 喝清茶能减肥吗,喝青茶能减肥吗
- 经期喝生姜茶可以么,经期喝什么茶
- 很少使用但很方便的HTML标签
