Android Dex文件详解
前言
相信大家都熟悉dex文件,把一个apk给解压缩,就会得到一堆dex文件,但是这些dex文件是怎么来的,又有什么用,为什么这样设计,有进行思考过吗
俗话说知其然,知其所以然,本篇文章开始探究一下这些底层实现细节。
正文
不同的虚拟机
JVM
JVM是Java Virtual Machine的简称,即Java虚拟机,它本质是一层软件抽象,在这之上才可以运行Java程序。Java文件经过编译后会生成JVM字节码,和C语言编译后生成的汇编语言不同,C编译成的汇编语言可以直接在硬件上跑,但是Java编译生成的字节码是在JVM上跑,需要由JVM把字节码翻译成机器指令。
也是由于这个JVM在操作系统上屏蔽了底层实现的差异,从而有了Java的跨平台特性。
DVM
DVM是Dalvik Virtual Machine的简称,是Android4.4及以前使用的虚拟机,所有Android程序都运行在Android系统进程中,每个进程对应着一个Dalvik虚拟机实例。
JVM和DVM都提供了对对象生命周期管理,堆栈管理,安全和异常管理及垃圾回收等重要功能。
但是DVM却不能和JVM一样能直接运行Java字节码,它只能运行.dex文件,而这个.dex文件则是由Java字节码通过Android的dx工具生成的文件。
ART
ART是Android Runtime,在Android5.0开始使用ART虚拟机来替代Dalvik虚拟机,为什么Google要换Android程序运行的虚拟机呢 因为ART虚拟机更优秀。
前面说了Dalvik虚拟机会在APP打开时去运行.dex文件,而这个是实时的,也就是JIT特性(Just In Time),这也就会导致在启动APP时会先将.dex文件转换成机器码,这就导致了APP启动慢的问题。
而ART虚拟机有个很好的特性叫做AOT(ahead of time),这个特性可以在安装APK的时候将dex直接处理成可直接供ART虚拟机使用的机器码,ART虚拟机将.dex文件转换成可直接运行的.oat文件,而且ART虚拟机天生支持多dex,所以ART虚拟机可以很大提升APP的冷启动速度。
除了这个优点外,ART还提升了GC速度,提供功能更全面的Debug特性,但是缺点也就是APK安装速度慢,占用的空间多。
生成和查看dex文件
前面说了dex文件是给Android手机的虚拟机来使用的,所以我们来看看如何生成和查看一个dex文件。
先编写一个简单的.java文件:
public class HelloWorld { int a = 0; static String b = "HelloDalvik"; public int getNumber(int i, int j) { int e = 3; return e + i + j; } public static void main(String[] args) { int c = 1; int d = 2; HelloWorld helloWorld = new HelloWorld(); String sayNumber = String.valueOf(helloWorld.getNumber(c, d)); System.out.println("HelloDex!" + sayNumber); }
}
然后使用javac命令来编译.java文件为.class,注意这里必须使用Java 8,而不能使用Java 11,如下图专门使用Java 8编译的结果(原来Windows环境变量是Java 11,后续的dex解析有误):
有了.class文件后,就是Android的dx工具,该工具一般在下面目录:
//也就是sdk目录下的build-tools文件夹中
D:\Users\wayee\AppData\Local\Android\sdk\build-tools\30.0.3\dx.bat
使用dx工具对.class文件进行处理:
然后会生成一个.dex文件,直接打开这个dex文件它是十六进制编码的文件,看不出任何有用信息,这时就需要一个专门来看这个的工具,这里推荐使用 010 Editor 这个工具,直接把.dex文件拖入工具:
注意这里选择的模板就是DEX.bt,然后就可以按照DEX的格式来分析这些字节是什么意思了,所以看懂dex文件必须要了解DEX文件的格式。
Dex文件格式
看到这里就必须要清楚一个基本概念了,也就是平时使用Java编写的文件,这里给编译打包成dex文件,那这个dex文件就必须要包含这个Java文件的所有信息,那是按照Java文件顺序一行一行保存为字节码还是其他什么方式呢
所以想知道编译器是如何在编译Java文件后保存信息的,就必须要清楚Dex文件格式。
我们可以直接在刚刚010 Editor软件中看到Dex.bt即Dex文件的格式,其格式如下:
当然也可以去Android源码官网看一下Dex的格式:
看了上面dex文件的格式,其大致可以分为3个区域,分别是文件头、索引区和数据区,那我们就来挨个分析这几个区域有什么作用,以及是如何保存编译后的Java文件。
header 文件头
头文件它包含了这个dex文件的几乎所有信息,所以它的信息非常多,其格式如下:
然后这时就直接点击010 Editor下面的dex_header部分:
其中上面的红框就是文件头的数据,而下面的红框就是文件头的格式,我们来挨个分析一下。
1、Magic value,即魔数,这个就是用来失败dex这种文件的,可以判断当前的dex文件是否有效,其值是固定死的:
转换成ASCII也就是dex.035, 所以凡是dex文件都是这个开头,否则就是错误的dex文件。
2、checksum,dex文件的校验和,它可以判断dex文件是否损坏或者篡改,占用4个字节,注意这里是采用小字节序的编码方式,即低位上存储的就是低字节内容,可以看一下:
会发现这里的值和二进制保存是相反的。
3、SHA1签名,也就是把整个dex文件用SHA-1签名得到的一个值,占用20个字节。
4、fileSize,整个文件的大小,占用4个字节,看一下值这里是:
十进制是1204,换算成16进制就是4B4,我们再来看看这个dex文件的长度:
这里长度也是4B4。
5、headerSize,表示头结构的大小,占用4个字节,这个就不截图看了。
6、endian_tag,表示字节序,这里具体的值就2个,标准的.dex格式采用小段字节序,但具体实现可能会选择执行字节交换,所以这个改变就由这个tag来判断。
7、linkSize和linkOff,这2个字段指定了链接段的大小和文件偏移,通常情况下他们都是0,linkSize为0表示为静态链接。
从这里开始就会发现有off这个字段,这是啥意思呢,其实也就是文件偏移量,也就是从这个文件第多少位置开始表示的值。
8、mapOff,这个字段表示DexMapList的文件偏移,这里我们先不多介绍,后面再说,这里值是:
换成16进制就是414h。
9、stringIdsSize和stringIdsOff,这2个字段指定了dex文件中所有用到的字符串的个数和位置偏移,注意这里指的是位置偏移,而不是真正的字符串值。
我们来看看size是多少:
会发现一共有28个字符串,而其值的偏移从112开始,而这个112是不是有点熟悉,112是整个dex头的大小,也就说明在头部之后第一部分就是字符串索引,这里之所以叫做索引也很合理,从112开始的n个字节保存的是程序用到的字符串的偏移量,注意这里不是字符串,只是各个字符串的偏移量。
这时你可能会疑惑,这28个字符串的偏移量该如何存放以及值是多少,我们完全不用担心,还是打开010 Editor软件,选中数据结构是dex_string_ids即可:
会发现从70h开始,开始保存的每个字符串的偏移量,而这个偏移量对应的就是最后面部分的值,我们还是拿第一个字符串来说:
会发现这里的偏移量,我们转到偏移量会发现:
这里是用了10个字节来保存了一个字符串,这个字符串是"clinit",我们暂时不考虑这个字符串是啥意思,这里采用了一种叫做uleb的数据结构,来动态保存字符串长度,这里我们暂时不考虑细节。
其实从这个字符串保存的方法来看,我们已经能大概看出是如何保存的了。首先在头部保存字符串大小,以及字符串索引的偏移量,然后再遍历索引找到每个字符串。
比如上面我们的代码,在这里保存的字符串是如下:
10、typeIds和typeIdsOff,有了上面字符串保存的逻辑,这个就是类的类型的数量和位置偏移,也都是占用4个字节,我们还是来看看值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kW77mc6C-1653642835067)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b0eb703dded4459a2e3a9b282327f8f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
一共用到了9个类型,但是注意这里就没必要像保存字符串一样了,记录每个类型的偏移量,再去偏移的地方取值,这里类型的描述符已经在前面字符串变量中都进行描述过了,所以这里保存的是字符串的索引,我们来看看:
找到上面对应的偏移位置,我们发现第一个类型值是0x5,然后我们再去前面的字符串索引找到下标为5的字符串:
会发现这里的值是I,也就是第一个类型,依次类推,所有的类型如下:
会发现这几个类型的字符串描述在前面字符串列表中都保存过了,这样设计也可以减小查询操作、节省内存。
11、protoIdSize和protoIdOff,这个表示的是方法原型的个数和位置偏移,会发现上面dex文件中有7个方法原型,这里图就不截了,来看一下这7个方法原型都保存了哪些数据:
其实不难理解,想表示一个方法原型不外乎就是方法名、返回值和参数,其中参数可能是多个,所以会有多个类型索引,这里具体的数据结构就不细说了,大体意思理解即可,来看看7个方法原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9aNGJ56-1653642835081)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0de94620959b4b9e92178e7999594b2b~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
这里的方法也就是前面java文件中有使用到的。
12、fieldSize和fieldOff,这2个字段就比较简单了,表示java文件中字段的信息,从头部数据结构中会发现有3个字段,我们直接看一下字段索引的数据:
会发现到这里时,信息表示就变的简单了,因为你想表示一个字段,不外乎就是类型、其类的类型、以及自己描述的字符串,而由于前面我们已经得到了字符串索引和类型索引,所以这里数据结构中的值直接使用前面定义过的索引即可。
还是看一下定义的所有字段的值:
13、methodSize和methodOff,这2个字段就比较熟悉了,表示了方法,而方法的表示也是需要几个要点,比如方法所在的类、方法的声明以及方法名,而类在之前类型索引定义过了,方法声明也声明过了,以及方法名也就是之前定义的字符串索引,所以这里我们就不细看其数据结构了,直接看一下我们前面写的java文件有多少个方法:
这里一共有10个方法。
14、classDefsSize和classDefsOff,这2个字段表示类定义的相关信息,类的信息就比较多了,包括类的修饰符、父类、接口、注解、静态元素等等,我们也还是通过010 Editor来看一下class都保存了哪些信息:
可以发现还是有不少信息的。
到这里我们基本就可以把一个类的信息都整清楚了,我们使用一张图来表示:
上图虽然只是表示了文件头的信息,但是我们知道有了这些文件头的信息,根据偏移量便可以获取到其保存的值。
Dex文件格式总结
看了文件头的定义,并且明白其值的意义,便也就熟悉了整个Dex格式的保存原理,我们这里看一张图:
这里除了文件头还有索引区和数据区,其中索引区的偏移量已经在文件头中定义,而数据区则保存着类的定义以及索引区中的数据,而最下面的链接数据区则是一些静态库或者动态库的链接。
总结
本篇内容有点多,但是还是很好理解的,首先就是虚拟机,在Android系统的虚拟机需要读取dex文件,而这个dex文件是由我们编写的.java文件编译而来,所以dex文件应当保存.java文件的所有信息。
而保存这些信息的方法就像是文件头保存大致地址,索引区保存具体地址,数据区是真的地方,通过这种方式就可以完整的保存一个java文件的信息。
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
需要的直接点击文末小卡片可以领取哦!我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)
Android学习PDF+架构视频+面试文档+源码笔记
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
- Android学习的系统对应视频
- Android进阶的系统对应学习资料
- Android BAT大厂面试题(有解析)
领取地址:
Android Dex文件详解相关推荐
- Android - Manifest 文件 详解
Manifest 文件 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/20899281 Manifest可以定义应用程序及其 ...
- Android.mk文件详解介绍
Android.mk 编译文件是用来向 Android NDK描述你的 C,C++源代码文件的, 这篇文档描述了它的语法.在阅读下面的内容之前,假定你已经阅读了 docs/OVERVIEW.TXT ...
- Android.bp文件详解
本文文档在如下位置,如有需要可以下载: https://download.csdn.net/download/fanx9339/12542402 Android.bp文件是什么? Android.bp ...
- Android清单文件详解(三)----应用程序的根节点application
<application>节点是AndroidManifest.xml文件中必须持有的一个节点,它包含在<manifest>节点下.通过<application>节 ...
- android .so文件详解以及兼容性
Android 设备的CPU类型通常称为ABIs 问题描述 解决方法 1解决之前的截图 2解决后的截图 3解决方法 4建议 为什么你需要重点关注so文件 App中可能出错的地方 其他地方也可能出错 使 ...
- Android AndroidManifest 文件详解
目录 一.概述 二.标签和属性 1.标签 2.属性 一.概述 AndroidManifest 文件,简称为 Manifest 文件,在 AndroidManifest 文件中,它告诉系统我们 App ...
- Android rc 文件详解
应用中添加使用rc 0. 在Android.mk 同目录下新建文件haha.sh (文件名任意),执行shell 操作, 以下简单举例 #!/bin/sh rm -rf /system/etc/xxx ...
- Android 清单文件 详解
转载于:https://www.cnblogs.com/mohe/archive/2013/03/31/2991642.html
- 生成jni的android.mk,Android Studio 3.5版本JNI生成SO文件详解
学习在于记录,把自己不懂得容易忘记得记录下,才是最好得选择. 废话不多说,想要在Android开发中嵌入c/c++代码,直接开始如下步骤 1.创建需要调用的Java类 在你某个指定的包下创建如下类pa ...
- android 7 apk 安装程序,Android安装apk文件并适配Android 7.0详解
Android安装apk文件并适配Android 7.0详解 首先在AndroidManifest.xml文件,activity同级节点注册provider: android:name="a ...
最新文章
- linux安装virtualbox命令,在Linux中从命令行查找Virtualbox Version的方法
- 日常生活 -- 感悟
- extract local variale 和 jsp中查找选中内容的快捷键
- 0408~送给小伙伴的汉堡包
- C++中size_type类型详解
- 2019-12-17 drivers/clocksource/arm_arch_timer.c
- Spring Cloud消息驱动整合
- 内存泄露分析 - 收藏集 - 掘金
- 阿斯克码表ACSII对照表
- 文件另存为GBK编码格式
- 从excel表格生成ArcGIS Pro样式符号
- 任正非:管理上的灰色,是我们的生命之树
- 微信公众平台开发--入门了解
- Eclipse 工具上Springboot项目的简单 增删改查 的搭建
- s7五杀大数据英雄_王者七大记录,五杀最多的英雄竟然有他?这英雄跑的比关羽还快!...
- 第一行代码Android技巧1——知晓当前是在哪一个活动
- m2硬盘写入速度测试软件,实测:M2固态硬盘换个插槽传输速度竟然提升了约1000M!...
- 一次性修改多张图片尺寸
- java 有序的list_Java 中的 List —— 有序序列
- 1833. 雪糕的最大数量
热门文章
- 万能ABAP程序修改器
- bootstrap插件bootbox参数
- 生成登录验证码,点击更换验证码图片
- 【开源项目分享】使用select、多线程完成的多人联机对战五子棋小游戏(C语言实现)
- 【python实战】制作微信动态名片
- 旅游B2B2C系统解决方案
- 解决qt.qpa.xcb: could not connect to display问题
- qt.qpa.plugin: Could not find the Qt platform plugin “xcb“ i
- Maven的setting仓库配置
- 【福利】小米手机修改MAC地址教程