之前寫了一篇我這段時間對組件化和插件化研究工作的整體概述,沒看過的小伙伴可以去回顧下我這段時間研究插件化的歷程:
插件化踩坑之路——Small和Atlas方案對比
Android插件化基礎(chǔ)篇 — dex 文件
Android 插件化基礎(chǔ)——ClassLoader 源碼解析
Android 插件化基礎(chǔ)——虛擬機(jī)
下面我計(jì)劃通過一個系列的博客由淺入深的解析插件化的原理。我們學(xué)習(xí)插件化需要一些預(yù)備知識,這些預(yù)備知識的簡單介紹組成了基礎(chǔ)篇,當(dāng)然,這些知識你也沒必要做到非常精通,只需了解到一定程度即可理解插件化原理,畢竟每一個知識點(diǎn)要深入進(jìn)去的話都是可以展開成一本書的,但技多不壓身,有興趣的朋友還是可以深入研究下的。
基礎(chǔ)篇分為以下幾個知識點(diǎn):
- class 文件 和 dex 文件解析
- 虛擬機(jī)相關(guān)知識點(diǎn)
- ClassLoader 相關(guān)知識點(diǎn)
class 文件的定義
class 文件就是能夠被 JVM 識別,加載并且執(zhí)行的文件格式。從定義來看,class 文件沒有想象中的那么神秘,和其他格式如 txt,mp4 一樣,只是一種文件格式,它存儲的是我們應(yīng)用程序。
不止 Java 語言,很多其他語言,比如 Scala,Python,都可以生成 class 字節(jié)碼文件,被 JVM 識別和執(zhí)行。
如何生成一個 class 文件
通常我們有兩種方式來生成 class 文件。
第一種方式就是通過我們強(qiáng)大的 IDE 來生成 class 文件,無需我們操心步驟,像 eclipse 和 IDEA 或者是 Android 開發(fā)使用的 Android Studio 都是我們常用的可以自動生成 class 字節(jié)碼文件的 IDE,同時,我們能通過 IDE 來幫助我們執(zhí)行 class 文件,只需要簡單的點(diǎn)擊 IDE 中的 run 按鈕,即可執(zhí)行。
雖然強(qiáng)大的 IDE 簡化了我們程序員很多操作,方便和提高了我們的開發(fā)工作,但同時會讓我們難以理解生成 class 文件的真正流程,所以很多小伙伴在剛開始學(xué)習(xí) Java 的時候,老師都會建議我們使用終端的 javac 命令去生成 class 文件,通過 java 命令來執(zhí)行 class 文件。這就是我們的第二種方式。
例如現(xiàn)在控制臺所在文件目錄下有一個 Test.java 文件,我們通過 javac Test.java 來生成 Test.class 文件,通過 java Test 執(zhí)行,控制臺就能看到輸出結(jié)果了。
javac 可以指定很多參數(shù),如 -target 1.6 -source 1.6 ,可以指定 JDK 版本,通常我們指定一個比較低的 JDK,JDK版本是向下兼容的,這樣我們使用高版本 JDK 時候,也是沒有問題的。當(dāng)然還有別的指令,這里就不再贅述了。
class 文件的作用
class 文件的作用是記錄一個類文件的所有信息。這里我們要強(qiáng)調(diào)下這個所有信息,因?yàn)?class 文件所包含的類文件信息,是遠(yuǎn)遠(yuǎn)多余我們能看到的 java 源代碼中的信息。
比如說 java 中我們能使用 this、super ,但我們并沒有定義這些關(guān)鍵字,這是因?yàn)槲覀冊谏?class 字節(jié)碼的時候,虛擬機(jī)幫我們記錄了當(dāng)前類 this 和父類 super 的信息,所以說 class 字節(jié)碼文件的信息量是遠(yuǎn)多于 java 源代碼的。
class 文件格式詳解
class 的文件結(jié)構(gòu)有以下幾個特點(diǎn):
- class 文件是一種 8 位字節(jié)的二進(jìn)制流文件。
- 各個數(shù)據(jù)是按順序緊密排列的,沒有任何間隙,這樣可以減少我們 class 文件的體積,讓 JVM 加載更加迅速。
- 每一個類、接口或者枚舉等,都會單獨(dú)占據(jù)一個 class 文件,這樣的好處可以類和接口等可以獨(dú)自管理自己的內(nèi)容而無需相互交叉提升管理的復(fù)雜性。
下圖是 class 文件的內(nèi)部結(jié)構(gòu)和所有的字段,我們將會具體解釋下每個字段的內(nèi)容。

- magic:無符號 4 字節(jié)類型,這個字段是 class 文件的加密端,用來檢測這個 class 文件有沒有篡改過,如果被篡改過了,JVM 將會有一系列的措施。有點(diǎn)類似于 MD5 加密。
- minor_version:最小 JDK 適配版本。
- major_version:當(dāng)前適配的 JDK 版本。
- constant_pool_count:常量池?cái)?shù)量,通常都是 1
- constant_pool:真正的常量池字段,結(jié)構(gòu)體類型,是我們 class 文件中最核心和也是比較難懂的部分。
- access_flags:當(dāng)前 class 文件作用域標(biāo)志,如 public , private , protected,這個比較好理解。
- this_class:JVM 幫我們填充的當(dāng)前類信息。
- super_class:JVM 幫我們填充的父類信息。
- interfaces_count:繼承的接口數(shù)量,只記錄直接繼承的接口。
- interfaces:記載繼承的接口,數(shù)量為 interfaces_count
- fields_count:標(biāo)明 class 文件中的成員變量的數(shù)量
- fields:結(jié)構(gòu)體類型,數(shù)量為 fields_count,包含成員變量的 Name,所屬的類和類型。
- methods_count:方法數(shù)量
- methods:結(jié)構(gòu)體類型,數(shù)量為 methods_count,記錄了方法的 name , type , access_flag等信息。
- attribute_count:以上沒有包含的信息的數(shù)量。
- attributes:結(jié)構(gòu)體類型,數(shù)量為 attribute_count,比如包含了注解等信息。
class 文件的這種文件結(jié)構(gòu)有點(diǎn)像 JSON,通過這樣一層套一層的結(jié)構(gòu),我們想要查閱什么類信息,都可以查閱的到。我們來看下 access_flags 的取值范圍,包括了我們平時使用到的所有類型,如下圖所示,不再具體說明:

下面我們將具體的講解下constant_pool字段。
constant_pool 中有一些比較簡單的字段,比如 CONSTANT_Integer_info,CONSTANT_Long_info,CONSTANT_String_info等,這些字段存儲了相對應(yīng)類型的信息,當(dāng)然還有像 Short,Double 這樣的其他類型。
CONSTANT_Class_info 記錄了類相關(guān)的信息,不僅記錄了當(dāng)前類的信息,如名字和 access_flags,還記錄了引用到的類的信息。還有 CONSTANT_Fieldref_info 記錄了成員變量的信息,CONSTANT_Methodref_info 則記錄了類中方法的信息。這些存儲的并不是真正的內(nèi)容,而是索引,指向之前說的那些CONSTANT_Integer_info,CONSTANT_Long_info,CONSTANT_String_info,真正的內(nèi)容實(shí)際上是存儲于這些字段中的。
我們可以通過一個很棒的軟件來查看這些二進(jìn)制文件:010 Editor。
這款軟件很強(qiáng)大,作用主要是來查看二進(jìn)制文件,不僅可以看 class 文件,還能看 dex 文件,非常清晰的能看到這些文件的文件結(jié)構(gòu)。

有關(guān) class 的知識點(diǎn)就簡單的介紹到這里,下一篇將介紹 dex 文件
本文部分內(nèi)容參考于慕課網(wǎng)實(shí)戰(zhàn)課程「Android 應(yīng)用發(fā)展趨勢必備武器 熱修復(fù)與插件化」,有興趣的朋友可以付費(fèi)學(xué)習(xí)。
插件化實(shí)戰(zhàn)課程