Android中有哪几种ClassLoader?它们的作用和区别是什么?
Android 中的 ClassLoader 工作交给了 BaseDexClassLoader 来处理。
BaseDexClassLoader 及其子类
ClassLoader 是个抽象类,其直接子类有 BaseDexClassLoader 和 SecureClassLoader。
SecureClassLoader 的子类是 URLClassLoader,其只能用来加载 jar 文件,这在 Android 的 Dalvik/ART 上没法使用的。
BaseDexClassLoader 的子类是 PathClassLoader,DexClassLoader 和 InMemoryDexClassLoader。
PathClassLoader
PathClassLoader 在应用启动时创建,从 data/app/… 安装目录下加载 apk 文件。
在 Android 中,App 安装到手机后,apk 里面的 class.dex 中的 class 均是通过 PathClassLoader 来加载的。
DexClassLoader
对比 PathClassLoader 只能加载已经安装应用的 dex 或 apk 文件,DexClassLoader 则没有此限制,可以从 SD 卡上加载包含 class.dex 的 .jar 和 .apk 文件,这也是插件化和热修复的基础,在不需要安装应用的情况下,完成需要使用的 dex 的加载。
InMemoryDexClassLoader
InMemoryDexClassLoader 是在API26时新增的类加载器,继承自BaseDexClassLoader。
InMemoryDexClassLoader的构造函数调用其父类DexClassLoader的构造函数。
ByteBuffer数组构造了一个DexPathList,可用于内存中的DEX文件。
DelegateLastClassLoader
DelegateLastClassLoader是在API27时新增的类加载器,继承自PathClassLoader。
DelegateLastClassLoader实行最后查找策略。使用DelegateLastClassLoader来加载每个类或资源,使用一下查找顺序:
- 判断是否已经加载过该类。
- 搜索此类的类加载器是否加载过这个类
- 搜索与此类加载器的dexPath关联的dex文件列表,委托给指定的父类对象加载
简述ClassLoader的双亲委托模型
从 jdk 中的 ClassLoader 类的构造方法,其需要传入一个父类加载器,并持有该引用。
|
|
当类加载器收到加载器或资源的请求是,通常都是先委托给父类加载器加载,也就是说只有当父类加载器找不到指定的类或资源时,自身才会执行实际的类加载过程,具体加载过程如下:
- 源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。
- 父类加载器判断是否加载过程该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。
- 以此类推,直到祖父类加载器(引用类加载器)。
- 始祖类加载器判断是否加载过改 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并加载。如果加载成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。
- 始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果贼如失败,则委托给始祖加载器的孙类加载器。
- 以此类推,直到源 ClassLoader。
- 源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常
以下用图示的方式,更清楚的展示类加载的过程:

简述双亲委托模型在热修复领域的应用
一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,ClassLoader会按照顺序依次遍历数组加载类,根据双亲委托模型,若一个dex已经被加载过,则不会再加载之后重复的dex,因此,只要把有问题的类修复后,放到一个单独的dex,通过反射插入到dexElements数组的最前面,这样在ClassLoader加载的时候会先加载已经修复好的dex,后面有bug的dex就不会被加载了。