一、dex/class淺析
1、class與dex對比
類型 | class文件 | dex文件 |
---|---|---|
定義 | 能夠被jvm識別、加載並執行的文件格式 | 能夠被dvm識別、加載並執行的文件格式 |
如何生成 | 使用java命令(javac) | 使用java命令、dx命令 |
作用 | 記錄一個類文件的所有信息 | 記錄整個工程中所有類文件的信息 |
2、生成class與dex文件的指令
生成並運行class文件對於我們而言實在太熟悉了,這裏只演示dex文件的生成與運行。
以 Hello World 爲例:
public class Hello {
public static void main(String[] args){
System.out.println("Hello LQR!");
}
}
1)生成dex文件
生成dex文件需要用到dx指令,與java指令一樣,也是對應一個對應的程序來執行的,最好配置到環境變量中,具體可看文章末尾。
生成dex文件之前需要先生成class文件,所需指令如下:
javac -target 1.6 -source 1.6 Hello.java
dx --dex -- output Hello.dex Hello.class
2)運行dex文件
class文件的運行需要依賴jvm,同理,dex文件的運行需要依賴dvm,所以dex文件需要在Android上才能運行。所需指令如下:
adb push Hello.dex /storage/emulated/0
adb shell
dalvikvm -cp /sdcard/Hello.dex Hello
使用adb將dex文件放送到Android手機的SD卡目錄之後,再使用adb進入shell,運行dvm指令即可。
二、class文件結構深入
1、class文件結構:
- 一種8位字節的二進制流文件
- 各個數據按順序緊密的排列,無間隙
- 每個類或接口都單獨佔據一個class文件
2、class文件結構的詳解:
一個class文件,包含下面表格的所有字段,
類型 | 名稱 | 數量 | 說明 |
---|---|---|---|
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,無符號數,以u1、u2、u4、u8分別代表1個字節、2個字節、4個字節、8個字節的無符號數
2,表,以“_info”結尾,由多個無符號數或其它表構成的複合數據類型
3、class文件弊端:
由class文件結構(第3點)所導致
- 內存佔用大,不適合移動端
- 堆棧的加載模式,加載速度慢
- 文件IO操作多,類查找慢
基於以上幾個class文件的特點,又因爲移動端運存較小(以當年的移動端手機爲標準),class並不適合直接在移動端設備上運行。
二、dex文件結構深入
1、dex文件結構:
- 一種8位字節的二進制流文件
- 各個數據按順序緊密的排列,無間隙
- 整個應用中所有java源文件都放在一個dex中
2、dex文件結構的詳解:
dex文件與class文件的結構有很大的不同,如下圖所示:
對應的字段說明如下表所示:
數據名稱 | 解釋 |
---|---|
header | dex文件頭部,記錄整個dex文件的相關屬性 |
string_ids | 字符串數據索引,記錄了每個字符串在數據區的偏移量 |
type_ids | 類似數據索引,記錄了每個類型的字符串索引 |
proto_ids | 原型數據索引,記錄了方法聲明的字符串,返回類型字符串,參數列表 |
field_ids | 字段數據索引,記錄了所屬類,類型以及方法名 |
method_ids | 類方法索引,記錄方法所屬類名,方法聲明以及方法名等信息 |
class_defs | 類定義數據索引,記錄指定類各類信息,包括接口,超類,類數據偏移量 |
data | 數據區,保存了各個類的真實數據 |
link_data | 連接數據區 |
3、dex頭文件
下面是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篇文章進行查閱瞭解,這裏便不再囉嗦:
4、dex文件的優勢
dex文件的頭文件與索引區部分,保存了所有類及類中數據的索引,因此,dvm可通過這兩部分快速查找到對應類及數據,相對於直接運行class文件而言,效率上提升了不少。
三、dex與class兩者的異同
經過上面對class文件與dex文件的結構進行大概的瞭解之後,我們可以得出如下幾個結論:
- 本質上都是一樣的,dex是從class文件演變而來的。
- class文件存在許多冗餘信息,dex文件會去除冗餘,並整合。
四、其他
1、配置Android及dx環境變量
以mac爲例,windows請百度。
- 到用戶目錄下:
cd ~
- 打開.bash_profile文件
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命令看是否有命令反應即可。