类型 | class文件 | dex文件 |
---|---|---|
定义 | 可以被jvm识别、加载并执行的文件格式 | 可以被dvm识别、加载并执行的文件格式 |
如何生成 | 使用java命令(javac) | 使用java命令、dx命令 |
做用 | 记录一个类文件的全部信息 | 记录整个工程中全部类文件的信息 |
生成并运行class文件对于咱们而言实在太熟悉了,这里只演示dex文件的生成与运行。java
以 Hello World 为例:shell
public class Hello {
public static void main(String[] args){
System.out.println("Hello LQR!");
}
}
复制代码
生成dex文件须要用到dx指令,与java指令同样,也是对应一个对应的程序来执行的,最好配置到环境变量中,具体可看文章末尾。windows
生成dex文件以前须要先生成class文件,所需指令以下:bash
javac -target 1.6 -source 1.6 Hello.java
dx --dex -- output Hello.dex Hello.class
复制代码
class文件的运行须要依赖jvm,同理,dex文件的运行须要依赖dvm,因此dex文件须要在Android上才能运行。所需指令以下:jvm
adb push Hello.dex /storage/emulated/0
adb shell
dalvikvm -cp /sdcard/Hello.dex Hello
复制代码
使用adb将dex文件放送到Android手机的SD卡目录以后,再使用adb进入shell,运行dvm指令便可。测试
一个class文件,包含下面表格的全部字段,ui
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u4 | magic | 1 | 魔数,0xCAFEBAB |
u2 | minor_version | 1 | 次版本号 |
u2 | major_version | 1 | 主版本号 |
u2 | constant_pool_count | 1 | 常量池中常量个数 |
cp_info | constant_pool | constant_pool_count-1 | 表类型数据集合,即常量池中每一项常量都是一个表,共有11种结构各不相同的表结构数据 |
u2 | access_flags | 1 | 访问标志,用于识别类或接口层次的访问信息 |
u2 | this_class | 1 | 类索引,用于肯定这个类的全限定名 |
u2 | super_class | 1 | 父类索引,用于肯定这个类父类的全限定名 |
u2 | interfaces_count | 1 | 接口索引计数器 |
u2 | interfaces | interfaces_count | 接口索引集合,用来描述这个类实现了哪些接口 |
u2 | fields_count | 1 | 字段表计数器,即字段表集合中的字段表数据个数 |
field_info | fields | fields_count | 字段表集合,用于描述接口或类中声明的变量,包括类级别(static)和实例级别变量,不包括在方法内部声明的变量 |
u2 | methods_count | 1 | 方法表计数器,即方法表集合中的方法表数据个数 |
method_info | methods | methods_count | 方法表集合,方法表结构和字段表结构同样 |
u2 | attributes_count | 1 | 属性订数器 |
attribute_info | attributes | attributes_count | 在Class文件、属性表、方法表中均可以包含本身的属性表集合,用于描述某些场景的专有信息 |
1,无符号数,以u一、u二、u四、u8分别表明1个字节、2个字节、4个字节、8个字节的无符号数 2,表,以“_info”结尾,由多个无符号数或其它表构成的复合数据类型this
源自:JVM笔记5:Class文件结构spa
由class文件结构(第3点)所致使.net
基于以上几个class文件的特色,又由于移动端运存较小(以当年的移动端手机为标准),class并不适合直接在移动端设备上运行。
dex文件与class文件的结构有很大的不一样,以下图所示:
对应的字段说明以下表所示:
数据名称 | 解释 |
---|---|
header | dex文件头部,记录整个dex文件的相关属性 |
string_ids | 字符串数据索引,记录了每一个字符串在数据区的偏移量 |
type_ids | 相似数据索引,记录了每一个类型的字符串索引 |
proto_ids | 原型数据索引,记录了方法声明的字符串,返回类型字符串,参数列表 |
field_ids | 字段数据索引,记录了所属类,类型以及方法名 |
method_ids | 类方法索引,记录方法所属类名,方法声明以及方法名等信息 |
class_defs | 类定义数据索引,记录指定类各种信息,包括接口,超类,类数据偏移量 |
data | 数据区,保存了各个类的真实数据 |
link_data | 链接数据区 |
下面是dex头文件中字段详解,与class文件的结构有部分相同的地方,但由于一个dex文件中包含n个class文件,在头文件中须要对全部class进行标记及记录相关信息,故会多出一些不一样的字段。
字段名称 | 偏移值 | 长度 | 说明 |
---|---|---|---|
magic | 0x0 | 8 | 魔数字段,值为"dex\n035\0" |
checksum | 0x8 | 4 | 校验码 |
signature | 0xc | 20 | sha-1签名 |
file_size | 0x20 | 4 | dex文件总长度 |
header_size | 0x24 | 4 | 文件头长度,009版本=0x5c,035版本=0x70 |
endian_tag | 0x28 | 4 | 标示字节顺序的常量 |
link_size | 0x2c | 4 | 连接段的大小,若是为0就是静态连接 |
link_off | 0x30 | 4 | 连接段的开始位置 |
map_off | 0x34 | 4 | map数据基址 |
string_ids_size | 0x38 | 4 | 字符串列表中字符串个数 |
string_ids_off | 0x3c | 4 | 字符串列表基址 |
type_ids_size | 0x40 | 4 | 类列表里的类型个数 |
type_ids_off | 0x44 | 4 | 类列表基址 |
proto_ids_size | 0x48 | 4 | 原型列表里面的原型个数 |
proto_ids_off | 0x4c | 4 | 原型列表基址 |
field_ids_size | 0x50 | 4 | 字段个数 |
field_ids_off | 0x54 | 4 | 字段列表基址 |
method_ids_size | 0x58 | 4 | 方法个数 |
method_ids_off | 0x5c | 4 | 方法列表基址 |
class_defs_size | 0x60 | 4 | 类定义标中类的个数 |
class_defs_off | 0x64 | 4 | 类定义列表基址 |
data_size | 0x68 | 4 | 数据段的大小,必须4k对齐 |
data_off | 0x6c | 4 | 数据段基址 |
源自:Dex文件格式详解
对于class文件及dex文件的结构 均可以使用 “010 editor” 这个神器进行查看验证,网上也有相关的文章说明,有兴趣的道友可自行百度 或 访问以下2篇文章进行查阅了解,这里便再也不啰嗦:
dex文件的头文件与索引区部分,保存了全部类及类中数据的索引,所以,dvm可经过这两部分快速查找到对应类及数据,相对于直接运行class文件而言,效率上提高了很多。
通过上面对class文件与dex文件的结构进行大概的了解以后,咱们能够得出以下几个结论:
以mac为例,windows请百度。
cd ~
复制代码
open -e .bash_profile
复制代码
若是当前用户目录下没有.bash_profile,可使用 touch .bash_profile 自行建立
export ANDROID_HOME=/Users/lqr/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PATH=${PATH}:${ANDROID_HOME}/build-tools/27.0.3
复制代码
ANDROID_HOME与build-tools的值须要根据电脑的状况修改。
source .bash_profile
复制代码
在终端输入adb或dx命令看是否有命令反应便可。