Dalvik 可执行文件格式

本文档介绍了 .dex 文件的版式和内容,这类文件用于保存一系列类定义及其关联的辅助数据。

类型指南

名称 说明
byte 8 位有符号整数
ubyte 8 位无符号整数
short 16 位有符号整数,采用小端字节序
ushort 16 位无符号整数,采用小端字节序
int 32 位有符号整数,采用小端字节序
uint 32 位无符号整数,采用小端字节序
long 64 位有符号整数,采用小端字节序
ulong 64 位无符号整数,采用小端字节序
sleb128 有符号 LEB128,可变长度(见下文)
uleb128 无符号 LEB128,可变长度(见下文)
uleb128p1 无符号 LEB128 加 1,可变长度(见下文)

LEB128

LEB128(“Little-Endian Base 128”)表示任意有符号或无符号整数的可变长度编码。该格式借鉴了 DWARF3 规范。在 .dex 文件中,LEB128 仅用于对 32 位数字进行编码。

每个 LEB128 编码值均由 1-5 个字节组成,共同表示一个 32 位的值。每个字节均已设置其最高有效位(序列中的最后一个字节除外,其最高有效位已清除)。每个字节的剩余 7 位均为载荷,即第一个字节中有 7 个最低有效位,第二个字节中也是 7 个,依此类推。对于有符号 LEB128 (sleb128),序列中最后一个字节的最高有效载荷位会进行符号扩展,以生成最终值。在无符号情况 (uleb128) 下,任何未明确表示的位都会被解译为 0。

双字节 LEB128 值的按位图
第一个字节第二个字节
1 bit6 bit5 bit4 bit3 bit2 bit1 bit0 0 bit13 bit12 bit11 bit10 bit9 bit8 bit7

变体 uleb128p1 用于表示一个有符号值,其表示法是编码为 uleb128 的值加 1。这使得 -1 的编码(或被视为无符号值 0xffffffff)成为一个单字节(但没有任何其他负数),并且该编码在下面这些情况下非常实用:所表示的数值必须为非负数或 -1(或 0xffffffff);不允许任何其他负值(或不太可能需要使用较大的无符号值)。

以下是这类格式的一些示例:

编码序列 As sleb128 As uleb128 As uleb128p1
0000-1
01110
7f-1127126
80 7f-1281625616255

文件版式

名称 格式 说明
header header_item 标头
string_ids string_id_item[] 字符串标识符列表。这些是此文件使用的所有字符串的标识符,用于内部命名(例如类型描述符)或用作代码引用的常量对象。此列表必须使用 UTF-16 码位值按字符串内容进行排序(不采用语言区域敏感方式),且不得包含任何重复条目。
type_ids type_id_item[] 类型标识符列表。这些是此文件引用的所有类型(类、数组或原始类型)的标识符(无论文件中是否已定义)。此列表必须按 string_id 索引进行排序,且不得包含任何重复条目。
proto_ids proto_id_item[] 方法原型标识符列表。这些是此文件引用的所有原型的标识符。此列表必须按返回类型(按 type_id 索引排序)主要顺序进行排序,然后按参数列表(按 type_id 索引排序的各个参数,采用字典排序方法)进行排序。该列表不得包含任何重复条目。
field_ids field_id_item[] 字段标识符列表。这些是此文件引用的所有字段的标识符(无论文件中是否已定义)。此列表必须进行排序,其中定义类型(按 type_id 索引排序)是主要顺序,字段名称(按 string_id 索引排序)是中间顺序,而类型(按 type_id 索引排序)是次要顺序。该列表不得包含任何重复条目。
method_ids method_id_item[] 方法标识符列表。这些是此文件引用的所有方法的标识符(无论文件中是否已定义)。此列表必须进行排序,其中定义类型(按 type_id 索引排序)是主要顺序,方法名称(按 string_id 索引排序)是中间顺序,而方法原型(按 proto_id 索引排序)是次要顺序。该列表不得包含任何重复条目。
class_defs class_def_item[] 类定义列表。这些类必须进行排序,以便所指定类的父类和已实现的接口比引用类更早出现在该列表中。此外,对于在该列表中多次出现的同名类,其定义是无效的。
call_site_ids call_site_id_item[] 调用站点标识符列表。这些是此文件引用的所有调用站点的标识符(无论文件中是否已定义)。此列表必须按 call_site_off 以升序进行排序。
method_handles method_handle_item[] 方法句柄列表。此文件引用的所有方法句柄的列表(无论文件中是否已定义)。此列表未进行排序,而且可能包含将在逻辑上对应于不同方法句柄实例的重复项。
数据 ubyte[] 数据区,包含上面所列表格的所有支持数据。不同的项有不同的对齐要求;如有必要,则在每个项之前插入填充字节,以实现所需的对齐效果。
link_data ubyte[] 静态链接文件中使用的数据。本文档尚未指定本区段中数据的格式。此区段在未链接文件中为空,而运行时实现可能会在适当的情况下使用这些数据。

容器格式

41 版中为 DEX 数据引入了一种新的容器格式,目的是节省空间。这种容器格式允许将多个逻辑 DEX 文件合并到单个实体文件中。新格式主要是对采用以前格式的文件进行简单的串联,不过有一些不同之处:

  • file_size 是逻辑文件的大小,而不是物理文件的大小。它可用于迭代容器中的所有逻辑文件。
  • 逻辑 dex 文件可以引用容器中的任何后续数据(但不能引用早期数据)。这样,dex 文件就可以在它们之间共享数据(例如字符串)。
  • 所有偏移量均相对于物理文件。没有相对于标头的偏移量。这可确保能够在逻辑文件之间共享具有偏移量的部分。
  • 标头添加了两个新字段来描述容器的边界。这是一种额外的一致性检查,有助于更轻松地将代码移植到新格式。
  • data_size 和 data_off 现已被弃用。数据可以分布在多个逻辑文件中,并且可以不连续。

位字段、字符串和常量定义

DEX_FILE_MAGIC

嵌在 header_item 中

常量数组/字符串 DEX_FILE_MAGIC 是字节列表,这类字节必须出现在 .dex 文件的开头,以便系统将其原样识别。该值会特意包含一个换行符("\n" 或 0x0a)和空字节("\0" 或 0x00),以便协助检测某些形式的损坏问题。该值还可以将格式版本号编码为 3 个十进制数字;随着格式的演变,预计该值会单调递增。

ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x39 0x00 }
                        = "dex\n039\0"

注意:Android 10.0 版本中增加了对 040 版格式的支持,该格式扩展了 SimpleNames 中允许使用的字符集。

注意:Android 9.0 版本中新增了对 039 版格式的支持,其中引入了两个新字节码 const-method-handle 和 const-method-type。(字节码集合的总结表中介绍了这些字节码。)在 Android 10 中,版本 039 扩展了 DEX 文件格式,以包含仅适用于启动类路径上的 DEX 文件的隐藏 API 信息。

注意:Android 8.0 版本中新增了对 038 版格式的支持。038 版本中添加了新字节码(invoke-polymorphic 和 invoke-custom)和用于方法句柄的数据。

注意:Android 7.0 版本中新增了对 037 版格式的支持。在 037 版本之前,大多数 Android 版本都使用过 035 版格式。035 版与 037 版之间的唯一区别是,是否添加默认方法以及是否调整 invoke。

注意:至少有两种早期版本的格式已在广泛提供的公开软件版本中使用。例如,009 版本已用于 M3 版 Android 平台(2007 年 11 月至 12 月),013 版本已用于 M5 版 Android 平台(2008 年 2 月至 3 月)。在有些方面,这些早期版本的格式与本文档中所述的版本存在很大差异。

ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT

嵌在 header_item 中

常量 ENDIAN_CONSTANT 用于表示在文件中发现的字节序。虽然标准的 .dex 格式采用小端字节序,但具体实现可能会选择执行字节交换。如果实现遇到其 endian_tag 为 ENDIAN_CONSTANT(而非 REVERSE_ENDIAN_CONSTANT)的头文件,则会识别该文件已从预期格式进行过字节交换。

uint ENDIAN_CONSTANT = 0x12345678;
uint REVERSE_ENDIAN_CONSTANT = 0x78563412;

NO_INDEX

嵌在 class_def_item 和 debug_info_item 中

常量 NO_INDEX 用于表示索引值不存在。

注意:此值不会被定义为 0,因为实际上此值通常是一个有效索引。

NO_INDEX 的选定值可表示为 uleb128p1 编码中的单个字节。

uint NO_INDEX = 0xffffffff;    // == -1 if treated as a signed int

access_flags 定义

嵌在 class_def_item、encoded_field、encoded_method 和 InnerClass 中

这些标志的位字段用于表示类和类成员的可访问性和整体属性。

名称 值 对于类(和 InnerClass 注释) 对于字段 对于方法
ACC_PUBLIC 0x1 public:全部可见 public:全部可见 public:全部可见
ACC_PRIVATE 0x2 *private:仅对定义类可见 private:仅对定义类可见 private:仅对定义类可见
ACC_PROTECTED 0x4 *protected:对软件包和子类可见 protected:对软件包和子类可见 protected:对软件包和子类可见
ACC_STATIC 0x8 *static:无法通过外部 this 引用构造 static:对定义类全局可见 static:不采用 this 参数
ACC_FINAL 0x10 final:不可子类化 final:构建后不可变 final:不可替换
ACC_SYNCHRONIZED 0x20     synchronized:调用此方法时自动获得关联锁定。

注意:这一项仅在同时设置 ACC_NATIVE 的情况下才有效。

ACC_VOLATILE 0x40   volatile:有助于确保线程安全的特殊访问规则  
ACC_BRIDGE 0x40     桥接方法,由编译器自动添加为类型安全桥
ACC_TRANSIENT 0x80   transient:不会通过默认序列化保存  
ACC_VARARGS 0x80     最后一个参数应被编译器解译为“rest”参数
ACC_NATIVE 0x100     native:在原生代码中实现
ACC_INTERFACE 0x200 interface:可多倍实现的抽象类    
ACC_ABSTRACT 0x400 abstract:不可直接实例化   abstract:不通过此类实现
ACC_STRICT 0x800     strictfp:严格的浮点运算规则
ACC_SYNTHETIC 0x1000 不在源代码中直接定义 不在源代码中直接定义 不在源代码中直接定义
ACC_ANNOTATION 0x2000 声明为注解类    
ACC_ENUM 0x4000 声明为枚举类型 声明为枚举值  
(未使用) 0x8000      
ACC_CONSTRUCTOR 0x10000     构造函数方法(类或实例初始化程序)
ACC_DECLARED_
SYNCHRONIZED
0x20000     声明了 synchronized。

注意:此项对执行没有任何影响(除了反映此标志本身之外)。

*仅允许用于 InnerClass 注解,且一律不得在 class_def_item 中使用。

修改了 UTF-8 编码

考虑到继续对旧版提供支持,.dex 格式会采用按实际标准修改后的 UTF-8 形式(下文称为 MUTF-8)对其字符串数据进行编码。除以下几点以外,此形式与标准 UTF-8 形式是完全相同的:

  • 仅使用单字节、双字节和三字节编码。
  • U+10000 … U+10ffff 范围中的码位被编码为代理对,其中每个码位均表示为一个三字节编码值。
  • 码位 U+0000 采用双字节格式形式编码。
  • 纯 null 字节(值 0)表示字符串的末尾,这是标准的 C 语言解释。

上述前两项可以概括为:MUTF-8 是用于 UTF-16 的编码格式,而不是用于 Unicode 字符的更直接的编码格式。

后两项说明,MUTF-8 既可在字符串中包含代码点 U+0000,同时仍可将其作为 C 样式的 Null 终止字符串进行操作。

不过,特殊编码 U+0000 意味着,与一般 UTF-8 不同的是,针对一对 MUTF-8 字符串调用标准 C 函数 strcmp() 的结果并不一定表示不相等字符串的带正确符号的比较结果。当需要考虑排序问题(而不仅仅是相等问题)时,用于比较 MUTF-8 字符串的最简单方法是对其字符进行逐个解码,然后比较解码后的值。(不过,也许会有更智能的实现方式。)

如需详细了解字符编码,请参阅 Unicode 标准。实际上,MUTF-8 比 UTF-8 本身更接近相对而言不太为人熟知的编码 CESU-8。

encoded_value 编码

嵌在 annotation_element 和 encoded_array_item 中

encoded_value 是(几乎)任意层次结构数据的编码片。这种编码非常精简,易于解析。

名称 格式 说明
(value_arg << 5) | value_type ubyte 一种字节,用于表示紧跟后面的 value 及高 3 位中可选澄清参数的类型。请参阅下文,了解各种 value 定义。在大多数情况下,value_arg 会以字节为单位将紧跟后面的 value 的长度编码为 (size - 1);例如,0 表示该值需要 1 个字节;7 表示该值需要 8 个字节;不过,也存在下述例外情况。
值 ubyte[] 用于表示值的字节,不同 value_type 字节的长度不同且采用不同的解译方式;不过一律采用小端字节序。如需了解详情,请参阅下文中的各种值定义。

值格式

类型名称 value_type value_arg 格式 value 格式 说明
VALUE_BYTE 0x00 (无;必须为 0) ubyte[1] 有符号的单字节整数值
VALUE_SHORT 0x02 size - 1 (0…1) ubyte[size] 有符号的双字节整数值,符号扩展
VALUE_CHAR 0x03 size - 1 (0…1) ubyte[size] 无符号的双字节整数值,零扩展
VALUE_INT 0x04 size - 1 (0…3) ubyte[size] 有符号的四字节整数值,符号扩展
VALUE_LONG 0x06 size - 1 (0…7) ubyte[size] 有符号的八字节整数值,符号扩展
VALUE_FLOAT 0x10 size - 1 (0…3) ubyte[size] 四字节位模式,向右零扩展,系统会将其解译为 IEEE754 32 位浮点值
VALUE_DOUBLE 0x11 size - 1 (0…7) ubyte[size] 八字节位模式,向右零扩展,系统会将其解译为 IEEE754 64 位浮点值
VALUE_METHOD_TYPE 0x15 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 proto_ids 区段的索引;表示方法类型值
VALUE_METHOD_HANDLE 0x16 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 method_handles 区段的索引;表示方法句柄值
VALUE_STRING 0x17 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 string_ids 区段的索引;表示字符串值
VALUE_TYPE 0x18 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 type_ids 区段的索引;表示反射类型/类值
VALUE_FIELD 0x19 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 field_ids 区段的索引;表示反射字段值
VALUE_METHOD 0x1a size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 method_ids 区段的索引;表示反射方法值
VALUE_ENUM 0x1b size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 field_ids 区段的索引;表示枚举类型常量的值
VALUE_ARRAY 0x1c (无;必须为 0) encoded_array 值的数组,采用下文“encoded_array 格式”所指定的格式。value 的大小隐含在编码中。
VALUE_ANNOTATION 0x1d (无;必须为 0) encoded_annotation 子注解,采用下文“encoded_annotation 格式”所指定的格式。value 的大小隐含在编码中。
VALUE_NULL 0x1e (无;必须为 0) (无) null 引用值
VALUE_BOOLEAN 0x1f 布尔值 (0…1) (无) 一位值;0 表示 false,1 表示 true。该位在 value_arg 中表示。

encoded_array 格式

名称 格式 说明
size uleb128 数组中的元素数量
values encoded_value[size] 采用本部分所指定格式的一系列 size encoded_value 字节序列;依序串联。

encoded_annotation 格式

名称 格式 说明
type_idx uleb128 注释的类型。这种类型必须是“类”(而非“数组”或“基元”)。
size uleb128 此注解中 name-value 映射的数量
elements annotation_element[size] 注解的元素,直接以内嵌形式(不作为偏移量)表示。元素必须按 string_id 索引以升序进行排序。

annotation_element 格式

名称 格式 说明
name_idx uleb128 元素名称,表示为要编入 string_ids 区段的索引。该字符串必须符合上文定义的 MemberName 的语法。
值 encoded_value 元素值

字符串语法

.dex 文件中有几种类型的项最终会引用字符串。以下 BNF 样式的定义指出了这些字符串可接受的语法。

SimpleName

SimpleName 是其他内容的名称语法的基础。SimpleName.dex 格式在这方面具有相当大的宽容度(比大多数常见源语言更大)。简而言之,一个简单的名称包含任意低 ASCII 范围的字母字符或数字、几个特定的低 ASCII 范围的符号,以及不属于控件、空格或特殊字符的大多数非 ASCII 码位。从 040 版开始,该格式额外允许使用空格字符(Unicode Zs 类别)。请注意,代理码位(位于 U+d800 … U+dfff 范围内)本身不会被视为有效名称字符,但 Unicode 补充字符会被视为有效字符(由 SimpleNameChar 规则的最终替代方案表示),它们应在文件中表示为 MUTF-8 编码中的代理码位对。

SimpleName →SimpleName
SimpleNameChar (SimpleNameChar)*
SimpleNameChar →SimpleNameChar
'A' … 'Z'
| 'a' … 'z'
| '0' … '9'
| ' ' 从 DEX 040 版开始
| '$'
| '-'
| '_'
| U+00a0 从 DEX 040 版开始
| U+00a1 … U+1fff
| U+2000 … U+200a 从 DEX 040 版开始
| U+2010 … U+2027
| U+202f 从 DEX 040 版开始
| U+2030 … U+d7ff
| U+e000 … U+ffef
| U+10000 … U+10ffff

MemberName

由 field_id_item 和 method_id_item 使用

MemberNameMemberName 是某个类的成员的名称,成员是指字段、方法和内部类。

MemberName →MemberName
SimpleName
| '<' SimpleName '>'SimpleName

FullClassName

FullClassNameFullClassName 是完全限定的类名称,包含一个可选的软件包说明符,后跟一个必需名称。

FullClassName →FullClassName
OptionalPackagePrefix SimpleName
OptionalPackagePrefix →OptionalPackagePrefix
(SimpleName '/')*SimpleName

TypeDescriptor

由 type_id_item 使用

TypeDescriptor 是任何类型的表示形式,包括基元、类、数组和 void。TypeDescriptor请参阅下文,了解各版本的含义。

TypeDescriptor →TypeDescriptor
'V'
| FieldTypeDescriptor
FieldTypeDescriptor →
NonArrayFieldTypeDescriptor
| ('[' * 1…255) NonArrayFieldTypeDescriptorNonArrayFieldTypeDescriptor
NonArrayFieldTypeDescriptor→NonArrayFieldTypeDescriptor
'Z'
| 'B'
| 'S'
| 'C'
| 'I'
| 'J'
| 'F'
| 'D'
| 'L' FullClassName ';'FullClassName

ShortyDescriptor

由 proto_id_item 使用

ShortyDescriptor 是方法原型的简写形式,包括返回类型和参数类型,只不过各种引用(类或数组)类型之间没有区别,ShortyDescriptor而是所有引用类型均由一个 'L' 字符表示。

ShortyDescriptor →ShortyDescriptor
ShortyReturnType (ShortyFieldType)*
ShortyReturnType →
'V'
| ShortyFieldType
ShortyFieldType →
'Z'
| 'B'
| 'S'
| 'C'
| 'I'
| 'J'
| 'F'
| 'D'
| 'L'

TypeDescriptor 语义

以下是 TypeDescriptor 各个变体的含义。TypeDescriptor

语法 含义
V void;仅对返回类型有效
Z boolean
B byte
S short
C char
I int
J long
F float
D double
Lfully/qualified/Name; 类 fully.qualified.Name
[descriptor descriptor 的数组,可递归地用于“数组的数组”,但维数不能超过 255。

项和相关结构

本部分包含可能在 .dex 文件中出现的各个顶级项的定义。

header_item

出现在 header 区段中

对齐:4 个字节

名称 格式 说明
magic ubyte[8] = DEX_FILE_MAGIC 魔法值。如需了解详情,请参阅上文中“DEX_FILE_MAGIC”下的讨论。
checksum uint 文件剩余内容(除 magic 和此字段之外的所有内容)的 adler32 校验和;用于检测文件损坏情况
签名 ubyte[20] 文件剩余内容(除 magic、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识
file_size uint

整个文件(包括标头)的大小,以字节为单位 (v40 或更低版本)

从此标头的开头到下一个标头或到整个文件(容器)的末尾的距离(以字节为单位)。 (v41 或更高版本)

header_size uint

头文件(整个区段)的大小,以字节为单位。这一项允许至少一定程度的向后/向前兼容性,而不必让格式失效。

必须为 0x70 (112) 字节 (v40 或更低版本)

必须为 0x78 (120) 字节 (v41 或更高版本)

endian_tag uint = ENDIAN_CONSTANT 字节序标记。如需了解详情,请参阅上文中“ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT”下的讨论。
link_size uint 链接区段的大小;如果此文件未进行静态链接,则该值为 0
link_off uint 从文件开头到链接区段的偏移量,如果 link_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 link_data 区段的偏移量。本文档未指定此处所指数据的格式;此标头字段(和之前的字段)会被保留为钩子,以供运行时实现使用。
map_off uint 从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 data 区段的偏移量,而数据应采用下文中“map_list”指定的格式。
string_ids_size uint 字符串标识符列表中的字符串数量
string_ids_off uint 从文件开头到字符串标识符列表的偏移量;如果 string_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 string_ids 区段开头的偏移量。
type_ids_size uint 类型标识符列表中的元素数量,最多为 65535
type_ids_off uint 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。
proto_ids_size uint 原型标识符列表中的元素数量,最多为 65535
proto_ids_off uint 从文件开头到原型标识符列表的偏移量;如果 proto_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 proto_ids 区段开头的偏移量。
field_ids_size uint 字段标识符列表中的元素数量
field_ids_off uint 从文件开头到字段标识符列表的偏移量;如果 field_ids_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 field_ids 区段开头的偏移量。
method_ids_size uint 方法标识符列表中的元素数量
method_ids_off uint 从文件开头到方法标识符列表的偏移量;如果 method_ids_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 method_ids 区段开头的偏移量。
class_defs_size uint 类定义列表中的元素数量
class_defs_off uint 从文件开头到类定义列表的偏移量;如果 class_defs_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 class_defs 区段开头的偏移量。
data_size uint

data 区段的大小(以字节为单位)。必须是 sizeof(uint) 的偶数倍。(v40 或更低版本)

未使用 (v41 或更高版本)

data_off uint

从文件开头到 data 部分开头的偏移量 (v40 或更低版本)

未使用 (v41 或更高版本)

container_size uint

此字段不存在。可以假定它等于 file_size。 (v40 或更低版本)

整个文件(包括其他 dex 标头及其数据)的大小。 (v41 或更高版本)

header_offset uint

此字段不存在。可以假定它等于 0。 (v40 或更低版本)

从文件开头到此标头开头的偏移量。 (v41 或更高版本)

map_list

出现在 data 区段中

引用自 header_item

对齐:4 个字节

这是文件全部内容(依序显示)的列表。该列表包含关于 header_item 的一些冗余信息,但这些冗余信息存在的目的是提供一种用于遍历整个文件的简单方式。指定类型在映射中最多只能出现一次,但除了格式其余部分所隐含的限制条件(例如,header 区段必须先显示,随后是 string_ids 区段等等)之外,对于类型可以什么顺序显示,则没有任何限制。此外,映射条目必须按初始偏移量进行排序,不得重叠。

名称 格式 说明
size uint 列表的大小(以条目数表示)
list map_item[size] 列表的元素

map_item 格式

名称 格式 说明
type ushort 项的类型;见下表
unused ushort (未使用)
size uint 在指定偏移量处找到的项数量
offset uint 从文件开头到相关项的偏移量

类型代码

项类型 常量 值 项大小(以字节为单位)
header_item TYPE_HEADER_ITEM 0x0000 0x70
string_id_item TYPE_STRING_ID_ITEM 0x0001 0x04
type_id_item TYPE_TYPE_ID_ITEM 0x0002 0x04
proto_id_item TYPE_PROTO_ID_ITEM 0x0003 0x0c
field_id_item TYPE_FIELD_ID_ITEM 0x0004 0x08
method_id_item TYPE_METHOD_ID_ITEM 0x0005 0x08
class_def_item TYPE_CLASS_DEF_ITEM 0x0006 0x20
call_site_id_item TYPE_CALL_SITE_ID_ITEM 0x0007 0x04
method_handle_item TYPE_METHOD_HANDLE_ITEM 0x0008 0x08
map_list TYPE_MAP_LIST 0x1000 4 + (item.size * 12)
type_list TYPE_TYPE_LIST 0x1001 4 + (item.size * 2)
annotation_set_ref_list TYPE_ANNOTATION_SET_REF_LIST 0x1002 4 + (item.size * 4)
annotation_set_item TYPE_ANNOTATION_SET_ITEM 0x1003 4 + (item.size * 4)
class_data_item TYPE_CLASS_DATA_ITEM 0x2000 隐式;必须解析
code_item TYPE_CODE_ITEM 0x2001 隐式;必须解析
string_data_item TYPE_STRING_DATA_ITEM 0x2002 隐式;必须解析
debug_info_item TYPE_DEBUG_INFO_ITEM 0x2003 隐式;必须解析
annotation_item TYPE_ANNOTATION_ITEM 0x2004 隐式;必须解析
encoded_array_item TYPE_ENCODED_ARRAY_ITEM 0x2005 隐式;必须解析
annotations_directory_item TYPE_ANNOTATIONS_DIRECTORY_ITEM 0x2006 隐式;必须解析
hiddenapi_class_data_item TYPE_HIDDENAPI_CLASS_DATA_ITEM 0xF000 隐式;必须解析

string_id_item

出现在 string_ids 区段中

对齐:4 个字节

名称 格式 说明
string_data_off uint 从文件开头到此项的字符串数据的偏移量。该偏移量应该是到 data 区段中某个位置的偏移量,且其中的数据应采用下文中“string_data_item”指定的格式。 没有偏移量对齐要求。

string_data_item

出现在 data 区段中

对齐:无(字节对齐)

名称 格式 说明
utf16_size uleb128 此字符串的大小;以 UTF-16 代码单元(在许多系统中为“字符串长度”)为单位。也就是说,这是该字符串的解码长度(编码长度隐含在 0 字节的位置)。
数据 ubyte[] 一系列 MUTF-8 代码单元(又称八位字节),后跟一个值为 0 的字节。请参阅上文中的“MUTF-8(修改后的 UTF-8)编码”,了解有关该数据格式的详情和讨论。

注意:将 Unicode 常规地编码为 UTF-16 时,字符串可以包含(编码形式的)UTF-16 代理代码单元(即 U+d800 … U+dfff),这些代码单元既可以单独出现,也可以乱序显示。在适用的情况下,可以进阶使用字符串来拒绝这类无效编码。

type_id_item

出现在 type_ids 区段中

对齐:4 个字节

名称 格式 说明
descriptor_idx uint 此类描述符字符串的 string_ids 列表中的索引。该字符串必须符合上文定义的 TypeDescriptor 的语法。TypeDescriptor

proto_id_item

出现在 proto_ids 区段中

对齐:4 个字节

名称 格式 说明
shorty_idx uint 此原型的简短式描述符字符串的 string_ids 列表中的索引。该字符串必须符合上文定义的 ShortyDescriptor 的语法,而且必须与该项的返回类型和参数相对应。ShortyDescriptor
return_type_idx uint 此原型的返回类型的 type_ids 列表中的索引
parameters_off uint 从文件开头到此原型的参数类型列表的偏移量;如果此原型没有参数,该值为 0。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“"type_list"”指定的格式。此外,不得对列表中的类型 void 进行任何引用。

field_id_item

出现在 field_ids 区段中

对齐:4 个字节

名称 格式 说明
class_idx ushort 此字段的定义符的 type_ids 列表中的索引。此项必须是“类”类型,而不能是“数组”或“基元”类型。
type_idx ushort 此字段的类型的 type_ids 列表中的索引
name_idx uint 此字段的名称的 string_ids 列表中的索引。该字符串必须符合上文定义的 MemberName 的语法。MemberName

method_id_item

出现在 method_ids 区段中

对齐:4 个字节

名称 格式 说明
class_idx ushort 此方法的定义符的 type_ids 列表中的索引。此项必须是“类”或“数组”类型,而不能是“基元”类型。
proto_idx ushort 此方法的原型的 proto_ids 列表中的索引
name_idx uint 此方法的名称的 string_ids 列表中的索引。该字符串必须符合上文定义的 MemberName 的语法。MemberName

class_def_item

出现在 class_defs 区段中

对齐:4 个字节

名称 格式 说明
class_idx uint 此类的 type_ids 列表中的索引。此项必须是“类”类型,而不能是“数组”或“基元”类型。
access_flags uint 类的访问标志(public、final 等)。如需了解详情,请参阅“access_flags 定义”。
superclass_idx uint 父类的 type_ids 列表中的索引。如果此类没有父类(即它是根类,例如 Object),该值为常量值 NO_INDEX。如果此类存在父类,此项必须是“类”类型,而不能是“数组”或“基元”类型。
interfaces_off uint 从文件开头到接口列表的偏移量;如果没有接口,该值为 0。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“type_list”指定的格式。该列表的每个元素都必须是“类”类型(而不能是“数组”或“基元”类型),并且不得包含任何重复项。
source_file_idx uint 文件(包含这个类(至少大部分)的原始来源)名称的 string_ids 列表中的索引;或者该值为特殊值 NO_INDEX,以表示缺少这种信息。任何指定方法的 debug_info_item 都可以替换此源文件,但预期情况是大多数类只来自一个源文件。
annotations_off uint 从文件开头到此类的注解结构的偏移量;如果此类没有注解,该值为 0。此偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“annotations_directory_item”指定的格式,同时所有项将此类作为定义符进行引用。
class_data_off uint 从文件开头到此项的关联类数据的偏移量;如果此类没有类数据,该值为 0(这种情况有可能出现,例如,如果此类是标记接口)。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“class_data_item”指定的格式,同时所有项将此类作为定义符进行引用。
static_values_off uint 从文件开头到 static 字段初始值列表的偏移量;如果没有该列表(并且所有 static 字段都将使用 0 或 null 进行初始化),该值为 0。此偏移量应位于 data 区段,且其中的数据应采用下文中“encoded_array_item”指定的格式。该数组的大小不得超出此类所声明的 static 字段的数量,且 static 字段所对应的元素应采用相对应的 field_list 中所声明的相同顺序。每个数组元素的类型均必须与其相应字段的声明类型相匹配。 如果该数组中的元素比 static 字段中的少,剩余字段将使用适当类型的 0 或 null 进行初始化。

call_site_id_item

出现在 call_site_ids 区段中

对齐:4 个字节

名称 格式 说明
call_site_off uint 从文件开头到调用点定义的偏移量。该偏移量应该位于数据区段,且其中的数据应采用下文中“call_site_item”指定的格式。

call_site_item

出现在 data 区段中

对齐:无(字节对齐)

call_site_item 是一个 encoded_array_item,其元素与提供给引导程序链接器方法的参数相对应。前 3 个参数为:

  1. 表示引导程序链接器方法的方法句柄 (VALUE_METHOD_HANDLE)。
  2. 引导程序链接器应解析的方法名称 (VALUE_STRING)。
  3. 与要解析的方法名称类型相对应的方法类型 (VALUE_METHOD_TYPE)。

任何附加参数均为传递给引导程序链接器方法的常量值。这些参数会按顺序传递,而不进行任何类型转换。

表示引导程序链接器方法的方法句柄必须具有返回类型 java.lang.invoke.CallSite。前 3 个参数类型为:

  1. java.lang.invoke.Lookup
  2. java.lang.String
  3. java.lang.invoke.MethodType

任何附加参数的参数类型均由其常量值确定。

method_handle_item

出现在 method_handles 区段中

对齐:4 个字节

名称 格式 说明
method_handle_type ushort 方法句柄的类型;见下表
unused ushort (未使用)
field_or_method_id ushort 字段或方法 ID 取决于方法句柄类型是访问器还是方法调用器
unused ushort (未使用)

方法句柄类型代码

常量 值 说明
METHOD_HANDLE_TYPE_STATIC_PUT 0x00 方法句柄是静态字段设置器(访问器)
METHOD_HANDLE_TYPE_STATIC_GET 0x01 方法句柄是静态字段获取器(访问器)
METHOD_HANDLE_TYPE_INSTANCE_PUT 0x02 方法句柄是实例字段设置器(访问器)
METHOD_HANDLE_TYPE_INSTANCE_GET 0x03 方法句柄是实例字段获取器(访问器)
METHOD_HANDLE_TYPE_INVOKE_STATIC 0x04 方法句柄是静态方法调用器
METHOD_HANDLE_TYPE_INVOKE_INSTANCE 0x05 方法句柄是实例方法调用器
METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR 0x06 方法句柄是构造函数方法调用器
METHOD_HANDLE_TYPE_INVOKE_DIRECT 0x07 方法句柄是直接方法调用器
METHOD_HANDLE_TYPE_INVOKE_INTERFACE 0x08 方法句柄是接口方法调用器

class_data_item

引用自 class_def_item

出现在 data 区段中

对齐:无(字节对齐)

名称 格式 说明
static_fields_size uleb128 此项中定义的静态字段的数量
instance_fields_size uleb128 此项中定义的实例字段的数量
direct_methods_size uleb128 此项中定义的直接方法的数量
virtual_methods_size uleb128 此项中定义的虚拟方法的数量
static_fields encoded_field[static_fields_size] 定义的静态字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。
instance_fields encoded_field[instance_fields_size] 定义的实例字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。
direct_methods encoded_method[direct_methods_size] 定义的直接(static、private 或构造函数的任何一个)方法;以一系列编码元素的形式表示。这些方法必须按 method_idx 以升序进行排序。
virtual_methods encoded_method[virtual_methods_size] 定义的虚拟(非 static、private 或构造函数)方法;以一系列编码元素的形式表示。此列表不得包括继承方法,除非被此项所表示的类覆盖。这些方法必须按 method_idx 以升序进行排序。虚拟方法的 method_idx 不得与任何直接方法相同。

注意:所有元素的 field_id 和 method_id 实例都必须引用相同的定义类。

encoded_field 格式

名称 格式 说明
field_idx_diff uleb128 此字段标识(包括名称和描述符)的 field_ids 列表中的索引;它会表示为与列表中前一个元素的索引之间的差值。列表中第一个元素的索引则直接表示出来。
access_flags uleb128 字段的访问标志(public、final 等)。如需了解详情,请参阅“access_flags 定义”。

encoded_method 格式

名称 格式 说明
method_idx_diff uleb128 此方法标识(包括名称和描述符)的 method_ids 列表中的索引;它会表示为与列表中前一个元素的索引之间的差值。列表中第一个元素的索引则直接表示出来。
access_flags uleb128 方法的访问标志(public、final 等)。如需了解详情,请参阅“access_flags 定义”。
code_off uleb128 从文件开头到此方法的代码结构的偏移量;如果此方法是 abstract 或 native,则该值为 0。偏移量应该是到 data 区段中某个位置的偏移量。数据格式由下文的“code_item”指定。

type_list

引用自 class_def_item 和 proto_id_item

出现在 data 区段中

对齐:4 个字节

名称 格式 说明
size uint 列表的大小(以条目数表示)
list type_item[size] 列表的元素

type_item 格式

名称 格式 说明
type_idx ushort type_ids 列表中的索引

code_item

引用自 encoded_method

出现在 data 区段中

对齐:4 个字节

名称 格式 说明
registers_size ushort 此代码使用的寄存器数量
ins_size ushort 此代码所用方法的传入参数的字数
outs_size ushort 此代码进行方法调用所需的传出参数空间的字数
tries_size ushort 此实例的 try_item 数量。如果此值为非零值,则这些项会显示为 insns 数组(正好位于此实例中 tries 的后面)。
debug_info_off uint 从文件开头到此代码的调试信息(行号 + 局部变量信息)序列的偏移量;如果没有任何信息,该值为 0。该偏移量(如果为非零值)应该是到 data 区段中某个位置的偏移量。数据格式由下文的“debug_info_item”指定。
insns_size uint 指令列表的大小(以 16 位代码单元为单位)
insns ushort[insns_size] 字节码的实际数组。insns 数组中的代码格式由随附文档 Dalvik 字节码指定。请注意,尽管此项被定义为 ushort 的数组,但仍有一些内部结构倾向于采用四字节对齐方式。另外,如果此项恰好位于某个字节序交换文件中,则交换操作将只在单个 ushort 实例上进行,而不在较大的内部结构上进行。
padding ushort(可选)= 0 使 tries 实现四字节对齐的两字节填充。只有 tries_size 为非零值且 insns_size 是奇数时,此元素才会存在。
tries try_item[tries_size](可选) 用于表示在代码中捕获异常的位置以及如何对异常进行处理的数组。该数组的元素在范围内不得重叠,且数值地址按照从低到高的顺序排列。只有 tries_size 为非零值时,此元素才会存在。
handlers encoded_catch_handler_list(可选) 用于表示“捕获类型列表和关联处理程序地址”的列表的字节。每个 try_item 都具有到此结构的分组偏移量。只有 tries_size 为非零值时,此元素才会存在。

try_item 格式

名称 格式 说明
start_addr uint 此条目涵盖的代码块的起始地址。该地址是到第一个所涵盖指令开头部分的 16 位代码单元的计数。
insn_count ushort 此条目所覆盖的 16 位代码单元的数量。所涵盖(包含)的最后一个代码单元是 start_addr + insn_count - 1。
handler_off ushort 从关联的 encoded_catch_hander_list 开头部分到此条目的 encoded_catch_handler 的偏移量(以字节为单位)。此偏移量必须是到 encoded_catch_handler 开头部分的偏移量。

encoded_catch_handler_list 格式

名称 格式 说明
size uleb128 列表的大小(以条目数表示)
list encoded_catch_handler[handlers_size] 处理程序列表的实际列表,直接表示(不作为偏移量)并依序串联

encoded_catch_handler 格式

名称 格式 说明
size sleb128 此列表中捕获类型的数量。如果为非正数,则该值是捕获类型数量的负数,捕获数量后跟一个“全部捕获”处理程序。例如,size 为 0 表示捕获类型为“全部捕获”,而没有明确类型的捕获。size 为 2 表示有两个明确类型的捕获,但没有“全部捕获”类型的捕获。size 为 -1 表示有一个明确类型的捕获和一个“全部捕获”类型的捕获。
handlers encoded_type_addr_pair[abs(size)] abs(size) 编码项的流(一种捕获类型对应一项),按照对类型进行测试时应遵循的顺序排列。
catch_all_addr uleb128(可选) “全部捕获”处理程序的字节码地址。只有当 size 为非正数时,此元素才会存在。

encoded_type_addr_pair 格式

名称 格式 说明
type_idx uleb128 要捕获的异常类型的 type_ids 列表中的索引
addr uleb128 关联的异常处理程序的字节码地址

debug_info_item

引用自 code_item

出现在 data 区段中

对齐:无(字节对齐)

每个 debug_info_item 都会定义一个基于 DWARF3 的字节码状态机,在解译时,该状态机会发出 code_item 的位置表,并可能会发出其局部变量信息。该序列的开头是一个可变长度的头文件(其长度取决于方法参数的数量),后跟状态机字节码,最后以 DBG_END_SEQUENCE 字节结尾。

该状态机由 5 个寄存器组成。address 寄存器表示关联的 insns_item 中的指令偏移量(以 16 位代码单元为单位)。address 寄存器在每个 debug_info 序列的开头处都是从 0 开始,而且只能单调递增。line 寄存器表示应与状态机发出的下一个位置表条目相关联的源行号。它在序列头文件中进行初始化,并可能会正向或负向发生更改,但绝不能小于 1。source_file 寄存器表示行号条目引用的源文件。它在 class_def_item 中被初始化为 source_file_idx 的值。另外两个变量(prologue_end 和 epilogue_begin)是布尔值标志(已初始化为 false),它们表示所发出的下一个位置是否应视为方法前序或结尾。此外,该状态机还必须跟踪用于 DBG_RESTART_LOCAL 代码的每个寄存器中最后一个局部变量的名称和类型。

标头如下所示:

名称 格式 说明
line_start uleb128 状态机的 line 寄存器的初始值。不表示实际的位置条目。
parameters_size uleb128 已编码的参数名称的数量。每个方法参数都应该有一个名称,但不包括实例方法的 this(如果有)。
parameter_names uleb128p1[parameters_size] 方法参数名称的字符串索引。NO_INDEX 的编码值表示该关联参数没有可用的名称。类型描述符和签名隐含在方法描述符和签名中。

字节码值如下:

名称 值 格式 参数 说明
DBG_END_SEQUENCE 0x00 (无) 终止 code_item 的调试信息序列
DBG_ADVANCE_PC 0x01 uleb128 addr_diff addr_diff:要添加到地址寄存器的数量 使地址寄存器指向下一个地址,而不发出位置条目
DBG_ADVANCE_LINE 0x02 sleb128 line_diff line_diff:要更改的行寄存器数量 使行寄存器指向下一行,而不发出位置条目
DBG_START_LOCAL 0x03 uleb128 register_num
uleb128p1 name_idx
uleb128p1 type_idx
register_num:将包含本地变量的寄存器
name_idx:该名称的字符串索引
type_idx:该类型的类型索引
在当前地址中引入一个本地变量。name_idx 或 type_idx 中的任何一个都可能是 NO_INDEX,表示该值未知。
DBG_START_LOCAL_EXTENDED 0x04 uleb128 register_num
uleb128p1 name_idx
uleb128p1 type_idx
uleb128p1 sig_idx
register_num:将包含本地变量的寄存器
name_idx:该名称的字符串索引
type_idx:该类型的类型索引
sig_idx:该类型签名的字符串索引
在当前地址中引入一个带有类型签名的本地变量。name_idx、type_idx 或 sig_idx 中的任何一个都可能是 NO_INDEX,用于表示该值是未知的。(即便 sig_idx 为 -1,使用操作码 DBG_START_LOCAL 也可以更有效地表示相同的数据。)

注意:请参阅下文“dalvik.annotation.Signature”下的讨论,了解关于处理签名的注意事项。

DBG_END_LOCAL 0x05 uleb128 register_num register_num:包含本地变量的寄存器 在当前地址将当前存在的局部变量标记为超出范围
DBG_RESTART_LOCAL 0x06 uleb128 register_num register_num:要重新启动的寄存器 在当前地址中重新引入一个本地变量。名称和类型与指定寄存器中存在的最后一个局部变量相同。
DBG_SET_PROLOGUE_END 0x07 (无) 设置 prologue_end 状态机寄存器;表示所添加的下一个位置条目应被视为方法前序的结尾(方法断点的适当位置)。prologue_end 寄存器会被任何特殊的 (>= 0x0a) 运算码清除。
DBG_SET_EPILOGUE_BEGIN 0x08 (无) 设置 epilogue_begin 状态机寄存器;表示所添加的下一个位置条目应被视为方法结尾的开头(要在方法退出之前暂停执行的适当位置)。epilogue_begin 寄存器会被任何特殊的 (>= 0x0a) 运算码清除。
DBG_SET_FILE 0x09 uleb128p1 name_idx name_idx:源文件名称的字符串索引;如果未知,则此项为 NO_INDEX 表示所有后续行号条目均引用此源文件名称,而不是 code_item 中指定的默认名称
特殊运算码 0x0a…0xff (无) 使 line 和 address 寄存器指向下一个地址,发出一个位置条目,并清除 prologue_end 和 epilogue_begin。请参阅下文中的说明。

特殊运算码

值在 0x0a 和 0xff(含)之间的操作码会将 line 和 address 寄存器移动一小部分,然后发出一个新的位置表条目。这些增量的公式如下所示:

DBG_FIRST_SPECIAL = 0x0a  // the smallest special opcode
DBG_LINE_BASE   = -4      // the smallest line number increment
DBG_LINE_RANGE  = 15      // the number of line increments represented

adjusted_opcode = opcode - DBG_FIRST_SPECIAL

line += DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE)
address += (adjusted_opcode / DBG_LINE_RANGE)

annotations_directory_item

引用自 class_def_item

出现在 data 区段中

对齐:4 个字节

名称 格式 说明
class_annotations_off uint 从文件开头到直接在该类上所做的注解的偏移量;如果该类没有任何直接注解,此值为 0。该偏移量(如果为非零值)应该是到 data 区段中某个位置的偏移量。数据格式由下文的“annotation_set_item”指定。
fields_size uint 此项所注释的字段数量
annotated_methods_size uint 此项所注释的方法数量
annotated_parameters_size uint 此项所注释的方法参数列表的数量
field_annotations field_annotation[fields_size](可选) 所关联字段的注释列表。该列表中的元素必须按 field_idx 以升序进行排序。
method_annotations method_annotation[methods_size](可选) 所关联方法的注释列表。该列表中的元素必须按 method_idx 以升序进行排序。
parameter_annotations parameter_annotation[parameters_size](可选) 所关联方法参数的注释列表。该列表中的元素必须按 method_idx 以升序进行排序。

注意:所有元素的 field_id 和 method_id 实例都必须引用相同的定义类。

field_annotation 格式

名称 格式 说明
field_idx uint 字段(带注解)标识的 field_ids 列表中的索引
annotations_off uint 从文件开头到该字段的注解列表的偏移量。偏移量应该是到 data 区段中某个位置的偏移量。数据格式由下文的“annotation_set_item”指定。

method_annotation 格式

名称 格式 说明
method_idx uint 方法(带注解)标识的 method_ids 列表中的索引
annotations_off uint 从文件开头到该方法注解列表的偏移量。偏移量应该是到 data 区段中某个位置的偏移量。数据格式由下文的“annotation_set_item”指定。

parameter_annotation 格式

名称 格式 说明
method_idx uint 方法(其参数带注解)标识的 method_ids 列表中的索引
annotations_off uint 从文件开头到该方法参数的注解列表的偏移量。偏移量应该是到 data 区段中某个位置的偏移量。数据格式由下文的“annotation_set_ref_list”指定。

annotation_set_ref_list

引用自 parameter_annotations_item

出现在 data 区段中

对齐:4 个字节

名称 格式 说明
size uint 列表的大小(以条目数表示)
list annotation_set_ref_item[size] 列表的元素

annotation_set_ref_item 格式

名称 格式 说明
annotations_off uint 从文件开头到所引用注释集的偏移量;如果此元素没有任何注释,则该值为 0。该偏移量(如果为非零值)应该是到 data 区段中某个位置的偏移量。数据格式由下文的“annotation_set_item”指定。

annotation_set_item

引用自 annotations_directory_item、field_annotations_item、method_annotations_item 和 annotation_set_ref_item

出现在 data 区段中

对齐:4 个字节

名称 格式 说明
size uint 该集合的大小(以条目数表示)
entries annotation_off_item[size] 该集合的元素。这些元素必须按 type_idx 以升序进行排序。

annotation_off_item 格式

名称 格式 说明
annotation_off uint 从文件开头到注解的偏移量。该偏移量应该是到 data 区段中某个位置的偏移量,且该位置的数据格式由下文的“annotation_item”指定。

annotation_item

引用自 annotation_set_item

出现在 data 区段中

对齐:无(字节对齐)

名称 格式 说明
visibility ubyte 此注释的预期可见性(见下文)
annotation encoded_annotation 已编码的注释内容,采用上文中“encoded_value 编码”下的“encoded_annotation 格式”所述的格式。

visibility 值

以下是 annotation_item 中 visibility 字段的选项:

名称 值 说明
VISIBILITY_BUILD 0x00 预计仅在构建(例如,在编译其他代码期间)时可见
VISIBILITY_RUNTIME 0x01 预计在运行时可见
VISIBILITY_SYSTEM 0x02 预计在运行时可见,但仅对基本系统(而不是常规用户代码)可见

encoded_array_item

引用自 class_def_item

出现在 data 区段中

对齐:无(字节对齐)

名称 格式 说明
值 encoded_array 用于表示已编码数组值的字节,采用上文中“encoded_value 编码”下的“encoded_array 格式”指定的格式。

hiddenapi_class_data_item

此部分包含每个类使用的受限接口的数据。

注意:隐藏的 API 功能是在 Android 10.0 中引入的,这些功能仅适用于启动类路径中类的 DEX 文件。未来版本的 Android 中可能会扩展下述标志列表。如需了解详情,请参阅针对非 SDK 接口的限制。

名称 格式 说明
size uint 区段的总大小
offsets uint[] 由 class_idx 编入索引的偏移量数组。索引 class_idx 中的零数组意味着此 class_idx 没有任何数据,或者所有隐藏 API 标记均为零。否则,数组条目为非零值,并且包含从区段开头到此 class_idx 的隐藏 API 标记数组的偏移量。
flags uleb128[] 每个类的隐藏 API 标志的级联数组。可能的标志值如下表所述。标志的编码顺序与字段和方法在类数据中的编码顺序相同。

限制标记类型:

名称 值 说明
whitelist 0 此列表中的接口已在 Android 框架软件包索引中正式记录,它们是受支持的接口,您可以自由使用。
greylist 1 包含可以使用的非 SDK 接口的列表(无论应用的目标 API 级别是什么)。
blacklist 2 包含不能使用的非 SDK 接口的列表(无论应用的目标 API 级别是什么)。访问其中任何一个接口都会导致运行时错误。
greylist‑max‑o 3 此列表中包含的是可用于 Android 8.x 及更低版本(除非受到限制)的非 SDK 接口。
greylist‑max‑p 4 此列表中包含的是可用于 Android 9.x(除非受到限制)的非 SDK 接口。
greylist‑max‑q 5 此列表中包含的是可用于 Android 10.x 的非 SDK 接口(除非这些接口受到限制)。
greylist‑max‑r 6 此列表中包含的是可用于 Android 11.x 的非 SDK 接口(除非这些接口受到限制)。

系统注释

系统注解用于表示关于类(以及方法和字段)的各种反射信息。这类信息通常只能通过客户端(非系统)代码来间接获取。

系统注解在 .dex 文件中表示为注解,可见性设为 VISIBILITY_SYSTEM。

dalvik.annotation.AnnotationDefault

出现在注解接口的方法中

AnnotationDefault 注解会附加到各个注解接口,用于表示默认绑定。

名称 格式 说明
值 注释 此注解的默认绑定,表示为此类型的注解。该注解不需要包含由注解定义的所有名称;缺少的名称根本没有默认值。

dalvik.annotation.EnclosingClass

出现在类中

EnclosingClass 注解会附加到各个类,这些类会被定义为其他类本身的成员,或者匿名显示但不在方法正文中定义(例如,合成的内部类)。具有此注解的各个类还必须具有 InnerClass 注解。此外,一个类不得同时具有 EnclosingClass 和 EnclosingMethod 注释。

名称 格式 说明
值 类 在词法作用域最接近此类的类

dalvik.annotation.EnclosingMethod

出现在类中

EnclosingMethod 注解会附加到已在方法正文中定义的各个类。具有此注解的各个类还必须具有 InnerClass 注解。 此外,一个类不得同时具有 EnclosingClass 和 EnclosingMethod 注释。

名称 格式 说明
值 方法 在词法作用域最接近此类的方法

dalvik.annotation.InnerClass

出现在类中

InnerClass 注解会附加到已在其他类定义的词法作用域中定义的各个类。具有此注解的所有类还必须具有 EnclosingClass 注解或 EnclosingMethod 注解。

名称 格式 说明
name 字符串 此类最初声明的简单名称(不包含任何软件包前缀)。如果此类是匿名类,则名称为 null。
accessFlags int 此类最初声明的访问标志(由于源语言与目标虚拟机之间的执行模型不匹配,该标志可能与有效标志不同)

dalvik.annotation.MemberClasses

出现在类中

MemberClasses 注解会附加到声明成员类(成员类是指具有名称的直接内部类)的各个类。

名称 格式 说明
值 Class[] 成员类的数组

dalvik.annotation.MethodParameters

出现在方法中

注意:此注释在 Android 7.1 之后的版本中添加。早期 Android 版本中存在的这类注释将被忽略。

MethodParameters 注解是可选项,并可用于提供参数元数据(例如参数名称和修饰符)。

当运行时不需要参数元数据时,可在方法或构造函数中放心地省略这项注解。java.lang.reflect.Parameter.isNamePresent() 可用于检查某个参数中是否存在元数据;如果不存在这种信息,关联的反射方法(例如 java.lang.reflect.Parameter.getName())将在运行时回退到默认行为。

如果编译器包含参数元数据,则必须包含已生成类(如枚举)的信息,因为参数元数据会指明参数是合成参数还是强制参数。

MethodParameters 注释仅用于描述单个方法的参数。因此,为了权衡代码大小和运行时效率,编译器可能会完全省略不含参数的构造函数和方法的注解。

下面记录的数组必须与该方法所关联的 method_id_item dex 结构具有相同的大小,否则运行时会抛出 java.lang.reflect.MalformedParametersException。

即:method_id_item.proto_idx -> proto_id_item.parameters_off -> type_list.size 必须与 names().length 和 accessFlags().length 相同。

由于 MethodParameters 说明了所有形式化方法参数,甚至包括源代码中没有明确声明或隐式声明的参数,因此数组的大小可能不同于签名或其他元数据信息(仅基于源代码中声明的显式参数)的大小。此外,MethodParameters 也不包含任何关于实际方法签名中不存在的类型注释接收器参数的信息。

名称 格式 说明
names String[] 关联方法的形式参数的名称。数组不得为空;但如果没有任何形式参数,该项必须为空。如果使用该索引的形式参数没有名称,数组中的值必须为 null。
如果参数名称字符串为空或包含“.”“;”“[”或“/”,运行时将会抛出 java.lang.reflect.MalformedParametersException。
accessFlags int[] 关联方法的形式参数的访问标志。数组不得为空;但如果没有任何形式参数,该项必须为 null。
该值是包含以下值的位掩码:
  • 0x0010:final,表示该参数被声明为 final
  • 0x1000:synthetic,表示该参数由编译器引入
  • 0x8000:mandated,表示该参数是合成参数,但同样由语言规范所暗示
如果任何位在此集合之外进行设置,运行时将会抛出 java.lang.reflect.MalformedParametersException。

dalvik.annotation.Signature

出现在类、字段和方法中

Signature 注解会附加到各个类、字段或方法(按照比 type_id_item 所表示的类型更复杂的类型进行定义)。.dex 格式不定义签名的格式;它仅仅能够表示源语言成功实现该语言的语义所需要的任何签名。因此,签名通常不会通过虚拟机实现进行解析(或验证)。这些签名只会传递给更高级别的 API 和工具(如调试程序)。因此,每次使用签名时都应该记录下来,以免就只接收有效签名作出任何假设,从而明确防止出现句法无效的签名。

由于签名字符串往往包含很多重复内容,因此 Signature 注解会被定义为字符串数组,其中的重复元素会自然地引用相同的基本数据,而签名则被视为该数组中所有字符串的串联。对于如何将签名分割成单独的字符串,没有相关规定;这完全取决于生成 .dex 文件的工具。

名称 格式 说明
值 String[] 此类或成员的签名,表示为要串联在一起的字符串的数组

dalvik.annotation.Throws

出现在方法中

Throws 注释会附加到已声明要抛出一个或多个异常类型的各个方法。

名称 格式 说明
值 Class[] 抛出的异常类型的数组