Dex文件格式
我们都知道Android项目在构建的时候,会将class文件的jar包通过dx工具将其转化成dex文件,目的是将所有的class文件整合到一个dex文件中,这样的目的是降低冗余,因为每个class的数据格式都相同,dex通过将相同的内容方法一起,使文件结构更加紧凑。
通过上图可以看出,dex文件将方法信息、字段信息、类型信息等都相同的信息都放到了一起,从而省下很多空间。
Dex文件组成(这里只介绍了重要部分):
官网查看全部结构
名称 | 说明 |
---|---|
header | dex文件头部,记录了整个dex文件相关信息的起点,很重要 |
string_ids | 存储dex信息相关内容的字符串在数据区的索引位置 |
type_ids | 存储的是类型(各种数据类型,比如类的类型,方法返回值类型等) |
proto_ids | 方法原型的描述,记录了方法的返回值类型,参数列表等 |
field_ids | 字段数据,记录了字段所属类型,类型等 |
method_ids | 记录方法信息,包括方法名,所属类名,方法原型等 |
class_defs | 存储的所有类的索引位置 |
data | 数据区,上面的所有数据都是里面获取的,以及存储dex的数据 |
数据类型
上面的数据类型是dex中的数据存储的相关数据的所有的数据类型,主要是其对应的字节长度,因为读取相关dex中对应的相关信息是通过其内容的数据类型,进入读取指定的长度的数据,也就是我们要的内容了。
上面唯一没有特殊的数据类型是LEB128,它没有固定的长度,他是1~5个字节可变的数据类型,主要是针对存储的数据是不定长度的。这种数据类型原理:首先判断第一个字节的最高位是否是1,如果是1,说明当前数据包括下一个字节;继续监测第二个字节的最高位是否为1,以此类推直到下一个字节的最高位是0,表示当前数据完毕。
官方文档
这种数据类型的数据读取方式,Android系统读取方式
源码路径: /dalvik/libdex/Leb128.h
DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
const u1* ptr = *pStream;
int result = *(ptr++); //取第一个字节
if (result > 0x7f) { //如果第1个字节大于0x7f,表示第一个字节最高位为1
int cur = *(ptr++); //第2个字节
result = (result & 0x7f) | ((cur & 0x7f) << 7); // &0x7f表示去掉这个这个字节的第8位。 合并第一位和第二位的数据
//下面依次读取第3,4,5位的数据
if (cur > 0x7f) {
cur = *(ptr++);
result |= (cur & 0x7f) << 14;
if (cur > 0x7f) {
cur = *(ptr++);
result |= (cur & 0x7f) << 21;
if (cur > 0x7f) {
/*
* Note: We don't check to see if cur is out of
* range here, meaning we tolerate garbage in the
* high four-order bits.
*/
cur = *(ptr++);
result |= cur << 28;
}
}
}
}
*pStream = ptr;
return