熱修復與插件化基礎——dex與class

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

源自:JVM筆記5:Class文件結構

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請百度。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章