當前位置:編程學習大全網 - 源碼下載 - Android類加載機制

Android類加載機制

Android手寫熱修復(壹)--ClassLoader

我們平時編寫的 .java 文件不是可執行文件,需要先編譯成 .class 文件才可以被虛擬機執行。所謂類加載是指通過 類加載器 把class文件加載到虛擬機的內存空間,具體來說是方法區。類通常是按需加載,即第壹次使用該類時才加載。

首先,Java與Android都是把類加載到虛擬機內存中,然後由虛擬機轉換成設備識別的機器碼。但是由於二者使用的虛擬機不同,所以在類加載方面也是有所區別的。Java的虛擬機是JVM,Android的虛擬機是dalvik/art(5.0以後虛擬機是art,是對dalvik的壹種升級)。 Java虛擬機運行的是class文件,而Android 虛擬機運行的是dex文件。 dex其實是class文件的集合,是對class文件優化的產物,是為了避免出現重復的class。

從上面的講解中,我們已經知道我們平時寫的類是被 類加載器 加載盡虛擬機內存才能運行。下面就通過Framework源碼來為大家講解Android中最主要的5個類加載器。

在Activity做個簡單驗證:

結果:

可以看出系統類由BootClassLoader加載,apk中的類由PathClassLoader加載,PathClassLoader的父類加載器是BootClassLoader。如果暫時不能理解父類加載器是什麽,沒關系,後面講雙親委托機制的時候會理解的。

下面的源碼解析基於 Android SDK API28 ,這幾個類加載器(除了ClassLoader)沒辦法直接在AS上查看源碼,AS搜索到的是反編譯的class的內容,是不可信的,為大家推薦壹個在線工具查看, 在線查看Android Framework源碼 。

用來加載本地文件系統上的文件或目錄,通常是用來加載apk中我們自己寫的類,而像 Activity.class 這種系統的類不是由它加載。註意:這裏,並不像很多網上文章說的那樣只能加載apk,本地的其他目錄的文件也是可以的,這壹點我會在後面驗證說明。

也是被用來加載 jar 、apk、dex,通常用來加載未安裝到應用中的文件。註意,它需要壹個應用私有的可寫的目錄來存放優化後的dex文件。千萬不要選擇外部存儲路徑,因為這樣可能會導致妳的應用遭到註入攻擊。

關於dex文件優化,可能很多人還是不理解,水平有限,我簡單解釋壹下,

構造器參數解釋:

關於optimizedDirectory:

1、這是dex優化後的路徑,它必須是壹個應用私有的可寫的目錄否則會存在註入攻擊的風險;

2、這個參數在API 26(8.0)之前是有值的,之後的話,這個參數已經沒有影響了,因為在調用父構造器的時候這個參數始終為null,也就是說Android 8.0 以後DexClassLoader和PathClassLoader基本壹樣的來;

3、在加載app的時候,apk內部的dex已經執行過優化了,優化之後放在系統目錄/data/dalvik-cache下。

這個構造器的關鍵是初始化了壹個DexPathList對象,這個是後面加載class的關鍵類。

這個構造方法等關鍵是通過 makeDexElements() 方法來獲取Element數組,這個Element數組非常關鍵,後面查找class就會用到它,也是熱修復的關鍵點之壹。

splitDexPath(dexPath) 方法是把dexPath目錄下的所有文件轉換成壹個File集合,如果是多個文件的話,會用 : 作為分隔符。

makeDexElements()

小結壹下,這個方法就是把指定目錄下的文件apk/jar/zip/dex按不同的方式封裝成Element對象,然後按順序添加到Element[]數組中。

DexPathList#loadDexFile()

可以看到 DexFile 最終是調用了openDexFile、native方法openDexFileNative去打開Dex文件的,如果outputName為空,則自動生成壹個緩存目錄,具體來說是 /data/dalvik-cache/xxx@classes.dex 。openDexFileNative這個native方法就不具體分析了,主要是對dex文件進行了優化操作,將優化後得odex文件通過mmap映射到內存中。感興趣的同學可以參考:

《DexClassLoader和PathClassLoader加載Dex流程》

現在在回頭看看DexClassLoader與PathClassLoader的區別。DexClassLoader可以指定odex的路徑,而PathClassLoader則采用系統默認的緩存路徑,在8.0以後沒有區別。

ClassLoader是壹個抽象類,有3個構造方法,最終調用的還是第壹個構造方法,主要功能是保存實現類傳入的parent參數,也就是父類加載器。ClassLoader的實現類主要有2個,壹個是前面講過的BaseDexClassLoader,另壹個是BootClassLoader。

BootClassLoader是ClassLoader的內部類,而且繼承了ClassLoader。

這是加載壹個類的入口,流程如下:

1、 先檢查這個類是否已經被加載,有的話直接返回Class對象;

2、如果沒有加載過,通過父類加載器去加載,可以看出parent是通過遞歸的方式去加載class的;

3、如果所有的父類加載器都沒有加載過,就由當前的類加載器去加載。

通常我們自己寫的類是通過當前類加載器調用 findClass 方法去加載的,但是在 ClassLoader 中這是個空方法,具體的實現在它的子類 BaseDexClassLoader 中。

BaseDexClassLoader # findClass

可以看到是通過pathList去查找class的,這個對象其實之前講過,它是在BaseDexClassLoader 的構造方法中初始化的,它實際上是壹個 DexPathList 對象。

DexPathList # findClass()

對Element數組遍歷,再通過Element對象的 findClass 方法去查找class,有的話就直接返回這個class,找不到則返回null。 這裏可以看出獲取Class是通過DexFile來實現的,而各種類加載器操作的是Dex。Android虛擬機加載的dex文件,而不是class文件。

1、加載壹個類是通過雙親委托機制來實現的。

2、如果是第壹次加載class,那是通過 BaseDexClassLoader 中的findClass方法實現的;接著進入 DexPathList 中的findClass方法,內部通過遍歷Element數組,從Element對象中去查找類;Element實際上是對Dex文件的包裝,最終還是從dexfile去查找的class。

3、壹般app運行主要用到2個類加載器,壹個是PathClassLoader:主要用於加載自己寫的類;另壹個是BootClassLoader:用於加載Framework中的類;

4、熱修復和插件化壹般是利用DexClassLoader來實現。

5、PathClassLoader和DexClassLoader其實都可以加載apk/jar/dex,區別是 DexClassLoader 可以指定 optimizedDirectory ,也就是 dex2oat 的產物 .odex 存放的位置,而 PathClassLoader 只能使用系統默認位置。但是在8.0 以後二者是沒有區別的,只能使用系統默認的位置了。

這張圖來源於:

Android虛擬機框架:類加載機制

在類加載流程分析中,我們已經知道,查找class是通過DexPathList來完成的,實際上DexPathList最終還是遍歷其Element數組,獲取DexFile對象來加載Class文件。 由於數組是有序的,如果2個dex文件中存在相同類名的class,那麽類加載器就只會加載數組前面的dex中的class。如果apk中出現了有bug的class,那只要把修復的class打包成dex文件並且放在 DexPathList 中Element數組`的前面,就可以實現bug修復了 。下壹篇為大家帶來的手寫熱修復。

Android類加載機制的細枝末節

從JVM到Dalivk再到ART(class,dex,odex,vdex,ELF)

類加載機制系列2——深入理解Android中的類加載器

Android 熱修復核心原理,ClassLoader類加載

  • 上一篇:如何使用python做android的自動化測試
  • 下一篇:機智雲智能電風扇智能化方案可以提供哪些服務?
  • copyright 2024編程學習大全網