Android 的 ClassLoader

简介: 文章目录1. Android 中有哪几种 ClassLoader?他们的作用和区别是什么?2. 简述 ClassLoader 的双亲委托模型3. 简述双亲委托模型在热修复领域的应用首先了解下 ClassLoader 是什么众所周知我们编写的 Java 程序编写的是 .

文章目录

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


img_f5463a515e27922e0fa81eb9a994f678.png
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 更灵活,是实现热修复的关键。

这个加载器只有一个构造方法 public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) 其中 optimizedDirectory 是用来缓存优化过的类或代码的, API 21 开始官方推荐放在 context.getCodeCacheDir() 目录

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 的类
目录
相关文章
|
存储 前端开发 Java
Android ClassLoader 源码阅读
Android ClassLoader 源码阅读
|
Android开发
【Android 逆向】类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )
【Android 逆向】类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )
255 0
【Android 逆向】类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )
|
安全 Java Android开发
【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )
【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )
245 0
【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )
|
Java Android开发
【Android 逆向】类加载器 ClassLoader ( 类加载时机 | 隐式加载 | 显示加载 | 类加载步骤 | 装载 | 链接 | 初始化 )
【Android 逆向】类加载器 ClassLoader ( 类加载时机 | 隐式加载 | 显示加载 | 类加载步骤 | 装载 | 链接 | 初始化 )
202 0
【Android 逆向】类加载器 ClassLoader ( 类加载时机 | 隐式加载 | 显示加载 | 类加载步骤 | 装载 | 链接 | 初始化 )
|
安全 Java Android开发
【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | ClassLoader#loadClass 函数分析 | BaseDexClassLoader#findClass 分析 )
【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | ClassLoader#loadClass 函数分析 | BaseDexClassLoader#findClass 分析 )
148 0
|
Java Android开发
【Android 逆向】类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )
【Android 逆向】类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )
245 0
|
前端开发 Java Android开发
【Android 逆向】类加载器 ClassLoader ( 启动类加载器 | 扩展类加载器 | 应用类加载器 | 类加载的双亲委托机制 )
【Android 逆向】类加载器 ClassLoader ( 启动类加载器 | 扩展类加载器 | 应用类加载器 | 类加载的双亲委托机制 )
146 0
|
Java Android开发
Android ClassLoader详解
我们知道不管是插件化还是组件化,都是基于系统的ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并,优化,然后生成一个最终的class.dex,目的是把不同class文件重复的东西只需保留一份,如果我们的Androi
2859 0
|
存储 缓存 Java
Android热修复技术初探(二):ClassLoader
在Java所编写的应用程序里,实质上都是ClassLoader来负责加载类的。有隐式加载类:如new一个实例对象,会自动触发ClassLoader来加载该类;有显示加载类:如直接调用ClassLoader的loadClass()方法来加载。
1079 0
|
前端开发 Java Android开发
Android插件化开发核心类ClassLoader相关详解
最近在研究插件化开发,顺便就了解了 ClassLoader 这个类加载器,顺藤摸瓜,查到了jvm里面的双亲委派模型,这里就简单的讲一下什么是预定义类加载器和双亲委派模型? 学好java基础,顺便学好jvm虚拟机,对阅读源码和插件化开发很有帮助。
1325 0