Android 面试—深入理解Android类加载机制

news2024/11/28 1:35:55

前言

任何一个java程序都是由一个或者多个class文件组成,在程序运行时,需要将class文件加载到JVM中才可以使用,负责加载这些class文件的就是java的类加载机制。ClassLoader的作用简单的来说就是加载class文件,提供给程序运行时使用,每个Class对象的内部都有一个ClassLoader字段来标识自己是由哪个Classloader加载的。

Java与Android类加载机制的区别

我们都知道JavaJVM虚拟机加载的是Class文件,而DVMART加载的是Dex文件,所以java的类加载器和Android的类加载器是不一样的。Java中的类加载器主要有系统加载器自定义加载器两种类型。系统类加载器主要是Bootstrap ClassLoaderExtensions ClassloaderApplication Classloader3种。Android中的Classloader类型和java中一样,也分为系统加载器自定义加载器两种。系统类加载器主要包括3种,分别是BootClassloaderPathClassloaderDexClassLoader这三种,接下来我们就来简单的了解一下Android中的类加载器。

Android中的类加载器

在这里插入图片描述

  • BaseDexClassLoader:实现应用层类文件的加载,真正的加载逻辑委托给PathList来完成。

  • PathClassLoader:继承自BaseDexClassLoader,加载系统类和应用程序的类,通常用来加载已安装的apkdex文件,实际上外部存储的dex文件也能加载。

  • DexClassLoader:继承自BaseDexClassLoader,可以加载dex文件以及包含dex的压缩文件(apk,dex,jar,zip),不管加载哪种文件,最终都要加载dex文件。Android8.0之后和PathClassloader无异。

  • BootClassLoaderAndroid系统启动时会使用BootClassLoader来预加载常用类,它继承自ClassLoader,是顶层的父加载器parent

PathClassLoader & DexClassLoader的异同

PathClassLoader构造方法:

public PathClassLoader(String dexPath,String librarySearchPath,ClassLoader parent){
	super(dexPath,null,librarySearchPath,parent);
}

DexClassLoader构造方法:

//android 8.0之前
public DexClassLoader(String dexPath, String optimizedDirectory,String librarySearchPath,ClassLoader parent){
	super(dexPath,new File(optimizedDiretory),librarySearchPth,parent);
}
//android 8.0之前
public DexClassLoader(String dexPath,String optimizedDirectory,String librarySearchPath,ClassLoader parent){
	super(dexPath,null,librarySearchPath,parent);
}
  • dexPath: dex文件以及包含dexapk文件或者jar文件的路径集合,多个路径用文件分隔符分隔,默认文件分隔符为“:”
  • optimizedDerectory: Android系统将dex文件进行优化后所生成的ODEX文件的存放路径,该路径必须是一个内部存储路径。在一般情况下,使用当前应用程序的私有路径:data/data/< Package Name> /...
  • librarySearchPath: 所使用到的C/C++库存放的路径
  • parent: 父加载器

从构造方法可以看出DexClassLoaderPathClassLoader的实现逻辑基本一样,其实二者都可以加载指定路径的apk、jar、zip、dex,区别在于DexClassLoader多了一个optimizedDirectory参数,optimizedDirectory参数就是dexopt的产出目录(odex)。那PathClassLoader创建时,这个目录为null,就意味着不进行dexopt?并不是,optimizedDirectorynull时的默认路径为:/data/dalvik-cacheoptimizedDirectory这个参数在API26的时候被谷歌废弃掉了,可以看到DexClassLoader中即使传递了这个参数,在super调用中,传递的值也是null,而且查看PathClassloaderDexClassLoadersuper调用,会发现代码是一样的,那么也就是说在Android8.0之后,这两个ClassLoader是没有区别的。

dex和odex区别: 一个APK是一个程序压缩包,里面有个执行程序包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件。因为你提取出来了,系统第一次启动的时候就不用去解压程序压缩包,少了一个解压的过程。这样的话系统启动就加快了。为什么说是第一次呢?是因为DEX版本的也只有第一次会解压执行程序到/data/dalvik-cache(针对PathClassLoader)或者optimizedDirectory(针对DexClassLoader)目录,之后也是直接读取目录下的的dex文件,所以第二次启动就和正常的差不多了。当然这只是简单的理解,实际生成的ODEX还有一定的优化作用。ClassLoader只能加载内部存储路径中的dex文件,所以这个路径必须为内部路径。

双亲委托介绍

类加载器在查找Class的时候采用的就是双亲委托机制,双亲委托就是在加载.Class文件的时候,首先会判断该Class文件是否被自身加载,如果没有加载的话会委托给父加载器parent去进行查找而不是让自身去加载,委托给父加载器之后父加载器会去判断自己是否有加载过这个文件,如果有加载,那么就直接返回,如果这个文件没有被加载的话,那么会继续向上委托给更上一级的父加载器去加载,直到达到链路的顶层ClassLoader,如果顶层ClassLoader也没有加载这个文件的话,那么顶层ClassLoader就会尝试自己去加载这个文件,如果加载失败,就会逐级向下交给它的子加载器加载这个文件,以此类推,如果最后都没有找到的话,才会交给自身去查找,其实双亲委派机制就是一个递归的过程。

Android双亲委派机制的实现

在这里插入图片描述

Android中的ClassLoaderjava中一样,同样遵循了双亲委托机制来加载,查看Classloader.java中的loadClass能够看出:

1、 首先调用findLoadedClass检查传入的类是否已经被加载,如果已经加载那么就直接返回。

2、 如果第一步中类没有被加载(c == null),那么就会判断parent是否等于null,也就是判断父加载器是否存在,如果父加载器存在,就调用父加载器的loadClass方法。

3、 如果父加载器不存在就会调用findBootstrapClassOrNull,这个方法会直接返回null

4、 如果到了第4步依然c == null,那么表示在向上委托的过程中,没有加载该类,会调用findClass继续向下进行查找。

接下来我们引用一个例子再总结说明一下整个加载流程:

在这里插入图片描述
上图中创建了一个DexClassLoader对象,使用DexClassLoader进行加载一个类,参数中的parent我们传入了context.getClassLoader,这里就等于DexClassLoaderparent我们给它传的是PathClassloader,所以此时的父子关系是DexClassLoader——>PathClassLoader——>BootClassLoader,也就是DexClassLoaderparentPathClassloaderPathClassloaderparentBootClassloader,需要注意的是这里的parent并不是继承关系,比如PathClassloader继承自BaseDexClassLoader,但是PathClassloaderparentBootClassLoader,二者并不冲突。下面使用一张流程图来进行总结。
在这里插入图片描述

双亲委派的作用

  • 避免重复加载,如果已经加载过一次Class,就不需要再次加载,而是直接读取已经加载的Class
  • 对于任意一个类确保在虚拟机中的唯一性,由加载它的类加载器和这个类的全类名一同确立其在Java虚拟机中的唯一性。不同的类加载器加载同一个class文件得到的不是同一个class对象
  • 安全,保证系统类.class文件不能被篡改。通过委托方式可以保证系统类的加载逻辑不会被篡改。假如我们自定义一个String类来替代系统的String类,就会造成安全隐患,但是使用双亲委托就会使得系统的String类在Java虚拟机启动时就被加载,也就无法通过自定义String类来替代系统的String类。
  • 只有当两个类名完全一致并且被同一个类加载器所加载的类,Java虚拟机才会认为它们是同一个类。

类的加载过程

可以看到在所有父ClassLoader无法加载Class时,则会调用自己的findClass()方法。其实任何ClassLoader子类,都可以重写loadClass()findClass() 。一般如果你不想使用双亲委托,则重写loadClass()修改其实现。而重写findClass()则表示在双亲委托下,父ClassLoader都找不到Class的情况下,定义自己如何去查找一个Class。如果没有重写的话,那findClass()是在BaseDexClassLoader中实现的。我们来看一下findClass()中的实现逻辑。

在这里插入图片描述
这里省略了部分代码,可以看到调用了pathListfindClass()方法,pathList就是DexPathList对象,在BaseDexClassLoader初始化的时候被创建。接下来进入DexPathList

在这里插入图片描述
可以看到DexPathList在创建的时候调用了makeDexElements()方法来创建出了dexElements数组,在makeDexElements之前我们先来看一下splitDexPath()方法,在这个方法中将dexPath目录下的所有程序文件转变成一个File集合,而且dexPath是一个用冒号作为分隔符把多个程序文件目录拼接起来的字符串,如 /data/dexdir1:/data/dexdir2:...makePathElements方法核心作用就是将指定路径中的所有文件转化成DexFile同时存储到到Element[]这个数组中。接下来进入DexPathListfindClass()方法中。

在这里插入图片描述

findClass()方法中会通过for循环不断的遍历dexElement数组,拿到element,然后调用elementfindClass()ElementDexPathList的内部类,dexElements是维护dex文件的数组, 每一个Element对应一个dex文件。DexPathList遍历dexElements,从每一个dex文件中查找目标类,在找到后即返回并停止遍历。继续往下看,进入elementfindClass()方法。

在这里插入图片描述
如果dexFile不等于空,就去查找类名与name相同的类,否则返回nulldexFile就是用来描述dex文件的,Dex的加载以及Class的查找,都是由该类调用它的native方法完成的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/423421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

结构体联合体sizeof内存求值 - 对齐数

讲解下struct和union的内存求值和对齐 以题目讲解 结构体联合体sizeof内存求值 - 对齐数不同位数下类型字节大小内存对齐规则struct 内存对齐求值嵌套struct内存对齐求值union的内存大小求值union大小计算准则struct嵌套union内存对齐求值不同位数下类型字节大小 一定要搞清楚…

【机器学习】P18 反向传播(导数、微积分、链式法则、前向传播、后向传播流程、神经网络)

反向传播反向传播反向传播中的数学导数与python链式法则简单神经网络处理流程从而理解反向传播神经网络与前向传播神经网络与反向传播反向传播 反向传播&#xff08;back propagation&#xff09;是一种用于训练神经网络的算法&#xff0c;其作用是计算神经网络中每个参数对损…

【Java虚拟机】JVM核心基础和常见参数实战

1.新版JVM内存组成部分和堆空间分布 JVM内存的5大组成&#xff08;基于JDK8的HotSpot虚拟机&#xff0c;不同虚拟机不同版本会有不一样&#xff09; 名称作用特点程序计数器也叫PC寄存器&#xff0c;用于记录当前线程执行的字节码指令位置&#xff0c;以便线程在恢复执行时能…

常见的DNS攻击与相应的防御措施

DNS查询通常都是基于UDP的&#xff0c;这就导致了在查询过程中验证机制的缺失&#xff0c;黑客很容易利用该漏洞进行分析。DNS服务可能面临如下DNS攻击风险&#xff1a; 黑客伪造客户端源IP地址发送大量的DNS请求报文&#xff0c;造成DNS request flood攻击。 黑客伪造成授权服…

MongoDB实现---存储机制

储存 GridFS机制 GridFS&#xff1a;将大文件分隔成多个小文档存放&#xff0c;这样我们能够有效的保存大文档&#xff0c;而且解决了BSON对象有限制的问题&#xff1b;通过两个集合实现&#xff1a;两个集合分别存储存储实际数据和存储文件的元数据&#xff1a; 元数据文件&…

一篇文章搞定《动手学深度学习》-(李沐)PyTorch版本的所有内容

目录 目录 简介 阅读指南 1. 深度学习简介 2. 预备知识 3. 深度学习基础 4. 深度学习计算 5. 卷积神经网络 6. 循环神经网络 7. 优化算法 8. 计算性能 9. 计算机视觉 10. 自然语言处理 环境 参考&#xff08;大家可以在这里下载代码&#xff09; 原书地址&#…

四结(4.13)多线程学习

今天又进行了一次学长授课&#xff0c;关于MySQL和JDBC本来学了增删查改操作&#xff0c;用IDEA也可以操作数据库&#xff0c;今天讲了一些数据库的规范&#xff08;三大范式&#xff09;、以及er图。了解了一下项目。 项目要分为客户端和服务端&#xff08;C/S&#xff09;来写…

vue面试题2023

1.$route和$router的区别? routes : 数组。 路由匹配规则 router &#xff1a; 对象。 路由对象 $router &#xff1a; 对象。 用于跳转路由 和 传递参数 $route &#xff1a;对象。 用于接收路由跳转参数 1.Vue的生命周期方法有哪些&#xff1f; - beforeCreate 初始化实…

科技云报道:“云减碳”成趋势,如何用“计算”帮助企业碳减排?

科技云报道原创。 将数据中心“丢进”水里、“扔进”山里&#xff0c;使用风能、太阳能等清洁能源为数据中心供电……这些都是通过物理方法为数据中心减碳。 数据中心的重要工作在于计算&#xff0c;计算导致了能源消耗&#xff0c;使其成为耗能大户&#xff0c;而通过云计算…

基于imx8m plus开发板全体系开发教程1:Windows/Linux 开发环境配置(连载中)

前言&#xff1a; i.MX8M Plus 开发板是一款拥有 4 个 Cortex-A53 核心&#xff0c;运行频率 1.8GHz;1 个 Cortex-M7 核心&#xff0c;运行频率 800MHz;此外还集成了一个 2.3 TOPS 的 NPU&#xff0c;大大加速机器学习推理。 全文所使用的开发平台均为与NXP官方合作的FS-IMX8…

Terraform 系列-什么是 IaC?

系列文章 &#x1f449; Terraform 系列文章 前言 聊到 Terraform, 必然绕不开 IaC 这个概念&#xff1f;那么&#xff0c;什么是 IaC? &#x1f914; 基本概念 基础架构即代码 (Infrastructure as Code, IaC) 是指通过代码而不是手动流程/控制台点击来管理和配置基础架构…

互联网+制造业:图扑数字孪生智慧工厂车间生产线

前言 随着信息技术、自动化技术和人工智能等技术的快速发展和应用&#xff0c;智能制造已成为全球制造业发展的主流趋势。智能制造是将智能化、自动化、数字化和网络化等技术手段运用到制造过程中&#xff0c;使生产过程具有高度智能化、自动化和数字化的特点&#xff0c;进而…

Linux安装宝塔,并实现公网远程登录宝塔面板【内网穿透】

文章目录前言1. 安装宝塔2. 安装cpolar内网穿透3. 远程访问宝塔4. 固定http地址5. 配置二级子域名6. 测试访问二级子域名转发自CSDN远程穿透的文章&#xff1a;Linux安装宝塔&#xff0c;并实现公网远程登录宝塔面板【内网穿透】 前言 宝塔面板作为建站运维工具&#xff0c;它…

网络编程2(套接字编程)

套接字编程UDP协议通信&#xff1a;TCP通信&#xff1a;套接字编程&#xff1a;如何编写一个网络通信程序 1.网络通信的数据中都会包含一个完整的五元组&#xff1a; sip&#xff0c;sport&#xff0c;dip&#xff0c;dport&#xff0c;protocol&#xff08;源IP&#xff0c;源…

计算机组成原理——第三章存储系统(上)

提示&#xff1a;吾与春风皆过客&#xff0c;君携秋水揽星河 文章目录前言3.1 存储系统基本概念3.2.1 主存储器的基本组成3.2.2 SRAM DRAM\3.2.3 只读存储器ROM3.3.1 主存储器与CPU的连接3.3.2 双端口RAM和多模块存储器前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c…

带你理解运算放大器

复习一下电子设计基本元器件&#xff0c;运算放大器 ...... 矜辰所致目录前言一、运放基本说明1.1 基本认识1.2 运放中的电流1.3 运放工作特性二、负反馈2.1 什么是负反馈&#xff1f;2.2 为什么要引入负反馈&#xff1f;负反馈电路分析2.3 正反馈三、提一下虚短与虚断结语前…

MAX14808 八通道3级电平 数字脉冲发生器

MAX14808八通道3级电平 高压(HV) 脉冲发生器&#xff0c;利用低压控制逻辑输入产生高频、高压双极性脉冲(高达105V) &#xff0c;用于驱动超声系统的压电传感器&#xff0c;MAX14808有八个集成的发送/接收(T/R)开关。 应用 超声医疗成像工业探伤检测压电驱动器测试设备 芯片…

RocketMQ 是是如何管理消费进度的?又是如何保证消息成功消费的?

RocketMQ 消费者保障 作者: 博学谷狂野架构师GitHub&#xff1a;GitHub 地址 &#xff08;有我精心准备的 130 本电子书 PDF&#xff09;只分享干货、不吹水&#xff0c;让我们一起加油&#xff01;&#x1f604; 消息确认机制 consumer 的每个实例是靠队列分配来决定如何消费消…

五、MyBatis各种查询功能

MyBatis的各种查询功能 如果查询出的数据只有一条&#xff0c;可以通过 实体类对象接收List集合接收Map集合接收 如果查询出的数据有多条&#xff0c;一定不能用实体对象接收&#xff0c;会抛TooManyResultsException&#xff0c;可以通过 实体类类型的List集合接收Map类型的L…

Python爬虫自动化从入门到精通第10天(Scrapy框架的基本使用)

Scrapy框架的基本使用Scrapy框架简介Scrapy框架的运作流程安装Scrapy框架Scrapy框架的基本操作Scrapy常用命令Scrapy框架简介 Scrapy框架主要包含以下组件&#xff1a; Scrapy Engine(引擎)&#xff1a;负责Spiders、Item Pipeline、Downloader、Scheduler之间的通信&#xf…