前言:
我们开发android写的都是java代码,java代码都是写在class里面,虚拟机需要把class文件加载进来才能创建实例对象并工作,完成类加载的角色就是ClassLoader。那这个ClassLoader是如保工作的呢?

1. 一个Android应用有几个ClassLoader实例?

一个运行的Android应用至少有2个ClassLoader,一个是BootClassLoader(系统启动时创建的),一个是PathClassLoader(应用启动时创建的,用于加载/data/data/packagename/apkname.apk)

2. 双亲代理模型

创建一个ClassLoader实列,需要使用一个现有的ClassLoader实例作为新创建的实例的Parent。

ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {if (parentLoader == null && !nullAllowed) {throw new NullPointerException("parentLoader == null && !nullAllowed");}parent = parentLoader;}

这样一个Android应用,甚至整个Android系统里所有的ClassLoader实例都会被一棵树关联起来,这也是ClassLoader的双亲代理模型(Parent-Delegation Model)的特点。

public Class<?> loadClass(String className) throws ClassNotFoundException {return loadClass(className, false);}protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {//1. 先查询当前ClassLoader实例是否加载过此类,有则直接返回Class<?> clazz = findLoadedClass(className);if (clazz == null) {ClassNotFoundException suppressed = null;try {//2.查找父类的ClassLoader是否加载过此类,有则返回clazz = parent.loadClass(className, false);} catch (ClassNotFoundException e) {suppressed = e;}if (clazz == null) {try {//3.若继承路线上的ClassLoader都没有加载,则由Child执行此类的加载工作clazz = findClass(className);} catch (ClassNotFoundException e) {e.addSuppressed(suppressed);throw e;}}}return clazz;}

这种从继承路上查询类加载实例,如果一个类被位于树根的ClassLoader加载过,在以后的整个系统的生命周期内,这个类永远不会被重新加载。

注意点:
在升级一些逻辑代码,通过动态加载dex文件获得新类替换原有的旧类时,为达到修复原有类的bug,就必须保证在加载新类的时候,旧类还没有被加载,若已加载过旧类,那么ClassLoader会一直优先使用旧类。
Java中判断一个类是否被加载过是通过判断
同一个Class = 相同的ClassName+PackageName+ClassLoader
所以如果使用不同的ClassLoader加载同一个类,也会造成类型不一样。

3.双亲代理模型的作用

1) 共享功能:一些Framework层级的类一旦被顶层的ClassLoader加载过就缓存在内存里,以后任何地方要用,都不需要重新加载;

2) 隔离功能:不同继承路线上的ClassLoader加载的类肯定不是同一个类,这样限制避免用户自己的代码冒充核心类库的类访问枋心库包里可见的成员变量。

4. android中两种类加载器

Google中的multidex方案解决了65535问题,即一个apk文件包含多个dex文件,除了主dex其他dex都是以资源的形式被加载进来。
前面说过Android虚拟机的类加载机制,同一个类只会被加载一次,所以热修复技术中,让修复后的类替换原有的类必须让补丁包的类优先被加载,插到原有dex之前。
android中的两个主要ClassLoader,都继承自BaseDexClassLoader,加载类都是通过findClass方法。

(1) PathClassLoder:加载系统的类和主dex中的类

我们看一下PathClassLoder类文件,发现里边只有两个构造函数

/**只能加载本地文件系统或是目录,但不能从网络加载,用来加载系统类和我们的应用程序*/
public class PathClassLoader extends BaseDexClassLoader {/*** @param dexPath jar/apk路径下包含的文件或类,多个用/分开* @param parent 加载器的父类*/public PathClassLoader(String dexPath, ClassLoader parent) {super(dexPath, null, null, parent);}/*** @param dexPath jar/apk中包含的类或资源,多个有/分隔* @param libraryPath 包含本地库的目录,多个用/分隔,可为空* @param parent 加载器的父类*/public PathClassLoader(String dexPath, String libraryPath,ClassLoader parent) {super(dexPath, null, libraryPath, parent);}}

PathClassLoader的两个构造函数都会直接调用父类构造器函数,最终加载类还是会调用BaseDexClassLoader中的findClass方法。

(2) DexClassLoader:加载其他类的加载器

/**加载包含了classex.dex对象的jar/apk文件,可用来加载没有安装的应用 */
public class DexClassLoader extends BaseDexClassLoader {/*** @param dexPath jar/apk中包含的类或资源,多个有/分隔* @param optimizedDirectory dex文件被加载后会被编译器优化,优化后dex存放的路径,不可为null* @param libraryPath 包含libraries的目录列表,多个可用/分隔,可为null* @param 父类构造器*/public DexClassLoader(String dexPath, String optimizedDirectory,String libraryPath, ClassLoader parent) {super(dexPath, new File(optimizedDirectory), libraryPath, parent);}}

可见两个类加载器都调用了父类的构造函数,只是PathClassLoader中的第二个参数为空,而DexClassLoader,第二个参数传的是new File(optimizedDirectory)。

我们再来看看父类BaseDexClassLoader的实现:

(3)BaseDexClassLoader

public class BaseDexClassLoader extends ClassLoader {private final DexPathList pathList;/*** @param dexPath jar/apk中包含的类或资源,多个有/分隔* @param optimizedDirectory dex文件被加载后会被编译器优化,优化后dex存放的路径,不可为null* @param libraryPath 包含libraries的目录列表,多个可用/分隔,可为null* @param 父类构造器*/public BaseDexClassLoader(String dexPath, File optimizedDirectory,String libraryPath, ClassLoader parent) {super(parent);this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {List<Throwable> suppressedExceptions = new ArrayList<Throwable>();Class c = pathList.findClass(name, suppressedExceptions);if (c == null) {ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);for (Throwable t : suppressedExceptions) {cnfe.addSuppressed(t);}throw cnfe;}return c;}}

从BaseDexClassLoader的构造函数中我们可以看出第二个参数optimizedDirectory是不能为空的,可是PathClassLoader里传的第二个参数为空,这按道理不符合调用原则。接着网下看,BaseDexClassLoader中的findClass方法调用的是DexPathList的findClass方法。

(4)DexPathList

我们再来看看DexPathList中的findClass方法

/**这个类中维护这一个dexElements的数组,在findClass的时候会遍历数组来查找*/
public Class findClass(String name, List<Throwable> suppressed) {for (Element element : dexElements) {DexFile dex = element.dexFile;if (dex != null) {Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);if (clazz != null) {return clazz;}}}if (dexElementsSuppressedExceptions != null) {suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));}return null;}
//给一个dexElements数组赋值,这个其实是存放我们的dex文件数组
this.dexElements =makeDexElements(splitDexPath(dexPath), optimizedDirectory);
//nativeLibraryDirectories就是lib库
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);

在DexPathList类中如果optimizedDirectory是用来缓存我们需要加载的dex文件的,并创建一个DexFile对象,如果它为null,那么会直接使用dex文件原有的路径来创建DexFile对象。因为应用安装后会进行优化,优化后的dex存在于/data/dalvik-cache目录下。

从代码中可以看出optimizedDirectory必须是一个内部存储路径,无论哪种动态加载,加载的可执行文件一定要存放在内部存储。DexClassLoader可以指定自己的optimizedDirectory,所以它可以加载外部的dex,因为这个dex会被复制到内部路径的optimizedDirectory;而PathClassLoader没有optimizedDirectory,所以它只能加载内部的dex,这些大都是存在系统中已经安装过的apk里面的。

我们再来看看DexPathList中的findClass方法,它其实遍历了之前所有的DexFile实例,其实就是遍历了所有加载过的dex文件,再调用dex.loadClassBinaryName方法

    public Class loadClassBinaryName(String name, ClassLoader loader) {return defineClass(name, loader, mCookie);}private native static Class defineClass(String name, ClassLoader loader, int cookie);

loadClassBinaryName调用了Native方法defineClass加载类。

通过上面的分析,我们可以得出以下结论:

  1. android中每个应用的类加载器至少有两个,一个是系统类加载器BootClassLoader用来加载系统类,一个是PathClassLoader用来加载应用的类;

  2. android中应用的类加载器主要有两种,分别是PathClassLoader和DexClassLoader,PathClassLoader只能用来加载已安装应用的dex文件,而DexClassLoader可以用来加载未安装的apk\java\dex等文件;

  3. android类加载会使用双亲代理模型进行加载类,主要是通过树型结构,先从child ClassLoader节点查找此类实例是否存在,不存在则到parent的CloassLoader中查找,存在返回,不存在再由child ClassLoder进行加载类。

android类加载器ClassLoader相关推荐

  1. 【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 拷贝 DEX 文件到内置存储 | 加载并执行 DEX 字节码文件 )

    文章目录 一.拷贝 Assets 目录下的 classes.dex 字节码文件到内置存储区 二.加载 DEX 文件并执行其中的方法 三.MainActivity 及执行结果 四.博客资源 一.拷贝 A ...

  2. 【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )

    文章目录 一.Android 类加载器 1.ClassLoader 抽象类 2.BootClassLoader 3.BaseDexClassLoader 4.PathClassLoader 5.Dex ...

  3. 【Android 逆向】类加载器 ClassLoader ( 启动类加载器 | 扩展类加载器 | 应用类加载器 | 类加载的双亲委托机制 )

    文章目录 一.类加载器 二.类加载的双亲委托机制 一.类加载器 Java 虚拟机 ClassLoader 类加载器 : Bootstrap ClassLoader : 启动类加载器 , 该 加载器由 ...

  4. java classloader详解_Java类加载器(ClassLoader)详解

    本文主要讲述Java ClassLoader的工作原理,这为后面将Android App代码热替换或者插件化升级做铺垫 一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class ...

  5. android 类加载器 DexClassLoader的用法,以及引出的插件架构

    1.android 类加载器(DexClassLoader的用法),调用其他apk的类中的方法: 方式一: 然后在Host中利用下面的方式调用 PackageManager pm = getPacka ...

  6. Java虚拟机学习 - 类加载器(ClassLoader)

    类加载器 类加载器(ClassLoader)用来加载 class字节码到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源文件在经过 Javac之后就被转换成 ...

  7. java类加载器_类加载器ClassLoader

    上篇文章说到,Class类可以通过一个类的全限定名去加载类,那么底层是如何去加载的呢?这就是我们今天要聊的类加载器ClassLoader,其可以通过一个类的全限定名来获取描述此类的二进制字节流,也即是 ...

  8. JVM之类加载器ClassLoader

    JVM之类加载器ClassLoader 本文目录 JVM简介 类加载器解析 1. JVM简介 ①. JVM是运行在操作系统之上的,它与硬件没有直接的交互 ②. JVM体系结构概览 注: 2. 类装载器 ...

  9. 类加载器ClassLoader的角色

    类加载器ClassLoader的角色

最新文章

  1. 设计模式学习笔记(十六:桥接模式)
  2. 3行代码实现从excel中读取出某列元素为所想要的元素集合中的所有行
  3. QCustomplot怎么实现对大数据量的自适应采样显示不卡顿
  4. QT解析 JSON 格式的数据
  5. SDUT-3364_欧拉回路
  6. JavaScript常用事件
  7. day1---流程控制语句的四种基本格式
  8. 如何使用Affinity Designer for mac编辑矢量曲线和形状
  9. python计算机视觉pdf百度云下载_Python计算机视觉编程(pdf+epub+mobi+txt+azw3)
  10. python怎么测试一个网站的延迟_Python检测网络延迟的代码
  11. 【ES 笔记】 ElasticSearch 基本的查询语句介绍
  12. 酒香还怕巷子深?如何打造一个优秀的GitHub开源项目
  13. r1音箱原生系统更改服务器,低门槛不破坏R1功能改AUX音源电脑喇叭[已更新完整]...
  14. Instant Neural Graphics Primitives with a Multiresolution Hash Encoding 翻译
  15. 【吾爱破解第一期】破解基础知识之认识壳与程序的特征
  16. 即将实习的应届毕业生 学习java SpringMVC 数据库 知识总结
  17. 蓝海创意云丨这六款软件,帮助制片高效规划拍摄计划
  18. 工作的意义,人生的意义,自由、快乐与幻觉
  19. 微信支付的回调函数实现验签以及解密
  20. GPT3.5插件免费使用方法(无须科学上网)

热门文章

  1. 添加更改管理计算机上的字体,如何添加、删除计算机的字体.doc
  2. 香港惊现“人才荒”!IT毕业生就业率近100%,起薪超2.3W!
  3. 抖音短视频开发,SDK包含的功能及技术实现方式
  4. Kotlin开发第三天,UI开发
  5. 准客呼,电销外呼管理系统,CRM管理
  6. java clob http传送,java之clob类型数据的处理
  7. 浙江工商大学计算机考研调剂,浙江工商大学2019年考研预调剂信息
  8. 智能排版助手 Gidot Typesetter 3.0.7 !
  9. 四维轻云地理空间数据云管理平台具有哪些优势?
  10. oracle19C 无法登录OEM 解决