Android 的 ClassLoader

Android 的 ClassLoader

2018-04-28
Android 基础
暂无
0

文章目录

  1. Android 中有哪几种 ClassLoader?他们的作用和区别是什么?
  2. 简述 ClassLoader 的双亲委托模型
  3. 简述双亲委托模型在热修复领域的应用

首先了解下 ClassLoader 是什么

众所周知我们编写的 Java 程序编写的是 .java 文件,但是在运行运行前会将代码编译成 .class 文件,以 Android Studio 项目为例在一般在项目目录下的 /build/intermediates/classes/debug 文件夹中有很多小文件夹,点进去看这些都是 .class 文件,程序在运行时就是加载这些 .class 文件,那么负责加载这些 .class 文件的就是我们的 ClassLoaer。

一般在 Android 中程序打包会把多个 .class 文件打包成一个或多个 .dex 文件

1. Android 中有哪几种 ClassLoader?它们的作用和区别是什么?

我们先看一下Android源码有几个 ClassLoader
《Android 的 ClassLoader》

我们发现 ClassLoader 是个抽象类并且有两个子类 SecureClassLoader 、BaseDexClassLoader

SecureClassLoader 的子类是 URLClassLoader ,其只能用来加载 jar 文件,然而 jar 文件在 Android 的 Dalvik/ART 上没法使用的。

BaseDexClassLoader 的子类是 PathClassLoader 、DexClassLoader 和 InMemoryDexClassLoader ( API 26 添加 )。

PathClassLoader 还有一个子类是 DelegateLastClassLoader ( API 27 添加 )

如果 jar 文件中包含 .dex 文件也是可以使用 BaseDexClassLoader 加载的

那我们就分析一下 BaseDexClassLoader 的几个子类

PathClassLoader

PathClassLoader 是一个简单的 ClassLoader 实现、Android 使用此类作为其系统类加载器和其应用程序类加载器。也就是说 PathClassLoader 在应用启动时创建,只能加载已经安装到 Android 系统中的apk文件(/data/app目录下,解压为 dex 后优化为 odex)。
经过代码调用发现 PathClassLoader 的 parent 是 java.lang.BootClassLoader 这应该是最上层的 ClassLoader。

DelegateLastClassLoader

DelegateLastClassLoader 是一个实现先查找再委托的类加载器实现

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> cl = findLoadedClass(name);
        if (cl != null) {
            return cl;
        }
        try {
            return Object.class.getClassLoader().loadClass(name);
        } catch (ClassNotFoundException ignored) {
        }
        ClassNotFoundException fromSuper = null;
        try {
            return findClass(name);
        } catch (ClassNotFoundException ex) {
            fromSuper = ex;
        }
        try {
            return getParent().loadClass(name);
        } catch (ClassNotFoundException cnfe) {
            throw fromSuper;
        }
    }
  1. 首先在当前加载器查找是否加载过这个类。
  2. 然后,尝试搜索此类的类加载器是否加载过这个类。
  3. 使用当前加载器尝试去加载类
  4. 最后委托给父加载器加载。

DexClassLoader

DexClassLoader 就不一样了他可以在可读取目录中加载 .dex 文件以及包含 .dex 的 .jar 、.zip 和 .apk 文件,比 PathClassLoader 更灵活,是实现热修复的关键。

InMemoryDexClassLoader

他的作用可以直接加载内存中的 dex 文件

2. 简述 ClassLoader 的双亲委托模型

这个问题我们从源码入手

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
            // 首先查找类是否加载过,加载过则返回类
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //让父类查找并加载类
                        c = parent.loadClass(name, false);
                    } else {
                        //查找由引导类加载器加载的类,如果未找到,则返回 NULL
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //
                    c = findClass(name);
                }
            }
            return c;
    }

(1)、首先查找当前类加载器是否加载过这个类,如果找到则返回这个类
(2)、如果找不到,就会调用父类的方法去查找是否加载过这个类,如果父类没有加载过就去查找祖父类加载器是否加载过,按照这样的逻辑一直查找到最上层的类加载器(始祖类加载器),因为始祖加载器没有 parent 了,就用引导类加载器去查找。
(3)、如果还没有找到就说明这个类 JVM 确实没有加载过,然后尝试使用引导类加载器加载这个类,如果成功则返回类,加载失败就再次尝试使用始祖类加载器,依次类推如果当前类加载器都加载失败则抛出异常

小结:这样做保证的类只会加载一次

3. 简述双亲委托模型在热修复领域的应用

热修复的原理就是要替换类文件,Android 的虚拟机( 基于寄存器 )与 Java 的虚拟机(基于栈)有些不同,它加载的不是 .class 字节码,而是 .dex 文件( 可以通过 Google 提供的 dx 工具对 .class 文件转换得到 ),所以上面提到的替换类文件就是等于替换 DEX 文件。

  1. 通过 PathClassLoader 来加载我们自身 App 的 dex 文件。
  2. 通过 DexClassLoader 来加载我们的没有 BUG 补丁dex 文件。
  3. 首先通过反射拿到分别两个 ClassLoader 的 < DexPathList pathList > ( 一个是我们自己应用的,另一个是我们补丁的 )
  4. 然后再次通过反射拿到分别两个 ClassLoader 中 pathList 里面的 <Element[] dexElements > 的值。
  5. 合并两个反射到的 Element 数组。( 需要把我们的补丁 dex 中的数组放在合并的数组最前面 )
  6. 将合并的新的数组,再次通过反射重新设置到我们自身 App 的 DexPathList 中,根据双亲委托模型类加载器会首先加载没有 BUG 的类
    原文作者:Android源码解析
    原文地址: http://blog.smallraw.com/archives/3/
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞