天天看點

Android ClassLoader詳解

我們知道不管是插件化還是元件化,都是基于系統的classloader來設計的。隻不過android平台上虛拟機運作的是dex位元組碼,一種對class檔案優化的産物,傳統class檔案是一個java源碼檔案會生成一個.class檔案,而android是把所有class檔案進行合并,優化,然後生成一個最終的class.dex,目的是把不同class檔案重複的東西隻需保留一份,如果我們的android應用不進行分dex處理,最後一個應用的apk隻會有一個dex檔案。

Android ClassLoader詳解

android中的classloader

android中類加載器有bootclassloader,urlclassloader,

pathclassloader,dexclassloader,basedexclassloader,等都最終繼承自java.lang.classloader。

java.lang.classloader是所有classloader的最終父類。構造方法主要以下兩種

1.傳入一個父類構造器

Android ClassLoader詳解

實際構造器

2.無參預設構造法

Android ClassLoader詳解
Android ClassLoader詳解
Android ClassLoader詳解

無參構造器

可以看出classloader主要就是傳入一個父構造器,而且一般父構造器不能為空,不像java虛拟機裡父構造器為空時預設的父構造器為bootstrap classloader。android中預設無父構造器傳入的情況下,預設父構造器為一個pathclassloader且此pathclassloader父構造器為bootclassloader。

classloader中重要的方法是loadclass(string name),其他的子類都繼承了此方法且沒有進行複寫。

Android ClassLoader詳解

可以看出在加載類時首先判斷這個類是否之前被加載過,如果有則直接傳回,如果沒有則首先嘗試讓parent classloader進行加載,加載不成功才在自己的findclass中進行加載。這和java虛拟機中常見的雙親委派模型一緻的,這種模型并不是一個強制性的限制模型,比如你可以繼承classloader複寫loadcalss方法來破壞這種模型,隻不過雙親委派模是一種被推薦的實作類加載器的方式,而且jdk1.2以後已經不提倡使用者在覆寫loadclass方法,而應該把自己的類加載邏輯寫到findclass中。

和java虛拟機中不同的是bootclassloader是classloader内部類,由java代碼實作而不是c++實作,是android平台上所有classloader的最終parent,這個内部類是包内可見,是以我們沒法使用。

隻能用于加載jar檔案,但是由于 dalvik 不能直接識别jar,是以在 android 中無法使用這個加載器。

先看下basedexclassloader的構造方式:

Android ClassLoader詳解

basedexclassloader的構造函數包含四個參數,分别為:

dexpath,指目标類所在的apk或jar檔案的路徑,類裝載器将從該路徑中尋找指定的目标類,該類必須是apk或jar的全路徑.如果要包含多個路徑,路徑之間必須使用特定的分割符分隔,特定的分割符可以使用system.getproperty(“path.separtor”)獲得。上面"支援加載apk、dex和jar,也可以從sd卡進行加載"指的就是這個路徑,最終做的是将dexpath路徑上的檔案odex優化到内部位置optimizeddirectory,然後,再進行加載的。

file optimizeddirectory,由于dex檔案被包含在apk或者jar檔案中,是以在裝載目标類之前需要先從apk或jar檔案中解壓出dex檔案,該參數就是制定解壓出的dex 檔案存放的路徑。這也是對apk中dex根據平台進行odex優化的過程。其實apk是一個程式壓縮包,裡面包含dex檔案,odex優化就是把包裡面的執行程式提取出來,就變成odex檔案,因為你提取出來了,系統第一次啟動的時候就不用去解壓程式壓縮包的程式,少了一個解壓的過程。這樣的話系統啟動就加快了。為什麼說是第一次呢?是因為dex版本的也隻有第一次會解壓執行程式到 /data/dalvik-cache(針對pathclassloader)或者optimizeddirectory(針對dexclassloader)目錄,之後也是直接讀取目錄下的的dex檔案,是以第二次啟動就和正常的差不多了。當然這隻是簡單的了解,實際生成的odex還有一定的優化作用。classloader隻能加載内部存儲路徑中的dex檔案,是以這個路徑必須為内部路徑。

libpath,指目标類中所使用的c/c++庫存放的路徑

classload,是指該裝載器的父裝載器,一般為目前執行類的裝載器,例如在android中以context.getclassloader()作為父裝載器。

在看下dexclassloader和pathclassloader的構造器:

Android ClassLoader詳解

dexclassloader支援加載apk、dex和jar,也可以從sd卡進行加載。

上面說dalvik不能直接識别jar,dexclassloader卻可以加載jar檔案,這難道不沖突嗎?其實在basedexclassloader裡對".jar",".zip",".apk",".dex"字尾的檔案最後都會生成一個對應的dex檔案,是以最終處理的還是dex檔案,而urlclassloader并沒有做類似的處理。

一般我們都是用這個dexclassloader來作為動态加載的加載器。

Android ClassLoader詳解

很簡單明了,可以看出pathclassloader沒有将optimizeddirectory置為null,也就是沒設定優化後的存放路徑。其實optimizeddirectory為null時的預設路徑就是/data/dalvik-cache 目錄。

pathclassloader是用來加載android系統類和應用的類,并且不建議開發者使用。

很多部落格裡說pathclassloader隻能加載已安裝的apk的dex,其實這說的應該是在dalvik虛拟機上,在art虛拟機上pathclassloader可以加載未安裝的apk的dex(在art平台上已驗證),然而在/data/dalvik-cache 确未找到相應的dex檔案,懷疑是art虛拟機判斷apk未安裝,是以隻是将apk優化後的odex放在記憶體中,之後進行釋放,這隻是個猜想,希望有知道的可以告知一下。因為dalvik上無法使用,是以我們也沒法使用。

可以看出,basedexclassloader中有個pathlist對象,pathlist中包含一個dexfile的數組dexelements,由上面分析知道,dexpath傳入的原始dex(.apk,.zip,.jar等)檔案在optimizeddirectory檔案夾中生成相應的優化後的odex檔案,dexelements數組就是這些odex檔案的集合,如果不分包一般這個數組隻有一個element元素,也就隻有一個dexfile檔案,而對于類加載呢,就是周遊這個集合,通過dexfile去尋找。最終調用native方法的defineclass。

art模式相比原來的dalvik,會在安裝apk的時候,使用android系統自帶的dex2oat工具把apk裡面的.dex檔案轉化成oat檔案,oat檔案是一種android私有elf檔案格式,它不僅包含有從dex檔案翻譯而來的本地機器指令,還包含有原來的dex檔案内容。這使得我們無需重新編譯原有的apk就可以讓它正常地在art裡面運作,也就是我們不需要改變原來的apk程式設計接口。art模式的系統裡,同樣存在dexclassloader類,包名路徑也沒變,隻不過它的具體實作與原來的有所不同,但是接口是一緻的。實際上,art運作時就是和dalvik虛拟機一樣,實作了一套完全相容java虛拟機的接口。

繼續閱讀