熱修復與外掛化基礎——dex與class

GitLqr發表於2018-05-13

一、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
複製程式碼

熱修復與外掛化基礎——dex與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指令即可。

熱修復與外掛化基礎——dex與class

二、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”結尾,由多個無符號數或其它表構成的複合資料型別

源自:JVM筆記5:Class檔案結構

3、class檔案弊端:

由class檔案結構(第3點)所導致

  • 記憶體佔用大,不適合移動端
  • 堆疊的載入模式,載入速度慢
  • 檔案IO操作多,類查詢慢

基於以上幾個class檔案的特點,又因為移動端運存較小(以當年的移動端手機為標準),class並不適合直接在移動端裝置上執行。

二、dex檔案結構深入

1、dex檔案結構:

  • 一種8位位元組的二進位制流檔案
  • 各個資料按順序緊密的排列,無間隙
  • 整個應用中所有java原始檔都放在一個dex中

2、dex檔案結構的詳解:

dex檔案與class檔案的結構有很大的不同,如下圖所示:

熱修復與外掛化基礎——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檔案會去除冗餘,並整合。

熱修復與外掛化基礎——dex與class

四、其他

1、配置Android及dx環境變數

以mac為例,windows請百度。

  1. 到使用者目錄下:
cd ~
複製程式碼
  1. 開啟.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的值需要根據電腦的情況修改。

  1. 配置生效
source .bash_profile
複製程式碼
  1. 測試

在終端輸入adb或dx命令看是否有命令反應即可。

相關文章