類型 | 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命令看是否有命令反應便可。