Android Classloader動(dòng)態(tài)加載分析

ClassLoader概念

我們知道赋秀,Java源文件(.java)經(jīng)過編譯器編譯之后钻注,會(huì)轉(zhuǎn)換成Java字節(jié)碼(.class)片仿,然而程序是如何加載這些字節(jié)碼文件到內(nèi)存中呢劲蜻?這就用到了ClassLoader陆淀,即類加載器。ClassLoader類加載器負(fù)責(zé)讀取 Java 字節(jié)代碼先嬉,并轉(zhuǎn)換成 java.lang.Class類的一個(gè)實(shí)例轧苫。從而只有class文件被載入到了內(nèi)存之后,才能被其程序所引用疫蔓。所以ClassLoader就是用來動(dòng)態(tài)加載class文件到內(nèi)存當(dāng)中用的含懊。

ClassLoader的分類

Android中的常用幾種類加載器類型繼承關(guān)系劃分可以用一組關(guān)系圖來表示

BootClassLoder

BootClassLoader是ClassLoader的內(nèi)部類,是包內(nèi)可見衅胀。通過查看ClassLoader源碼 我們得知岔乔,Android中默認(rèn)類加載器為PathClassLoder,而PathClassLoader的父類加載器正是BootClassLoader滚躯。所以我們無法直接使用BootClassLoader雏门,也無法直接動(dòng)態(tài)加載嘿歌。

  /**
     * Encapsulates the set of parallel capable loader types.
     */
    private static ClassLoader createSystemClassLoader() {
        String classPath = System.getProperty("java.class.path", ".");
        String librarySearchPath = System.getProperty("java.library.path", "");

        ...省略部分代碼

        //默認(rèn)父構(gòu)造器為PathClassLoder
        return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
    }

URLClassLoader

URLClassLoader繼承自SecureClassLoader,SecureClassLoader繼承自ClassLoader茁影。URLClassLoader的特點(diǎn)就是只能加載jar文件宙帝,但是dalvik不能直接識(shí)別jar。所以在Android中無法直接使用這個(gè)類加載器募闲。

BaseDexClassLoader

BaseDexClassLoader直接繼承自ClassLoader步脓,下面是其構(gòu)造函數(shù)

public BaseDexClassLoader(String dexPath, File optimizedDirectory,  String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
        ....
    }

下面解析下這四個(gè)參數(shù)

  • dexPath:指目標(biāo)類所在的apk、dex或者jar文件的路徑(包括SD卡)蝇更,然后加載器將從該路徑中尋找到指定的目標(biāo)類。當(dāng)然了呼盆,這個(gè)路徑可以是多個(gè)路徑年扩,這樣可以尋找到多個(gè)目標(biāo)類,多路徑之間需要使用特定的分隔符访圃,分隔符可以使用System.getProperty("path.separtor")獲取厨幻。

  • optimizedDirectory:由于dex文件被包含在apk或者jar文件中,需要先解壓出來腿时,而這個(gè)參數(shù) 就代表了被解壓的路徑况脆。而且apk文件其實(shí)也是一個(gè)壓縮包,解壓的過程其實(shí)也是一個(gè)ODEX優(yōu)化的過程批糟,那么何為ODEX優(yōu)化呢格了?其實(shí)就是把包里面的可執(zhí)行程序提取出來變成ODEX文件,存放到optimizedDirectory目錄下徽鼎,因?yàn)樘崛〕鰜淼脑蚴⒛瑧?yīng)用第一次進(jìn)行啟動(dòng)的時(shí)候,直接使用ODEX文件 啟動(dòng)速度自然是比解壓再啟動(dòng)速度是要快的否淤。為什么是應(yīng)用第一次啟動(dòng)呢悄但?因?yàn)閐ex版本只有第一次啟動(dòng)會(huì)解壓執(zhí)行程序到/data/dalvik-cache(針對(duì)PathClassLoader),或者optimizedDirectory文件目錄下(針對(duì)DexClassLoader)石抡,之后就可以直接讀取目錄下的dex文件了檐嚣。

  • librarySearchPath:指的是目標(biāo)類所使用的c、c++庫存放的路徑

  • parent:是指該加載器的父加載器啰扛,一般為當(dāng)前執(zhí)行類的加載器嚎京。

PathClassLoader

public PathClassLoader(String dexPath, ClassLoader parent) {  
        super(dexPath, null, null, parent);  
}  

 public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath,  null, librarySearchPath, parent);
     ...
  }

通過源碼我們可以知道,PathClassLoader繼承于BaseDexClassLoader隐解,并且構(gòu)造器將optimizedDirectory置為null挖藏,也就是沒有設(shè)置ODEX優(yōu)化后的存儲(chǔ)路徑,前文有提到厢漩,如果沒有設(shè)置optimizedDirectory目錄膜眠,那么默認(rèn)存儲(chǔ)路徑就是/data/dalvik-cache。因?yàn)檫@個(gè)原因,PathClassLoader被設(shè)定成只能加載Android系統(tǒng)類和已安裝的android應(yīng)用類宵膨。不過在art虛擬機(jī)上架谎,測(cè)試得知,PathClassLoader則不受此限制辟躏,和DexClassLoader加載范圍一致谷扣。

DexClassLoader

public DexClassLoader(String dexPath, String optimizedDirectory,  String librarySearchPath, ClassLoader parent) {
        super(dexPath,  new File(optimizedDirectory), librarySearchPath, parent);
     ...
    }

DexClassLoader也是繼承于BaseDexClassLoader,支持加載包含classes.dex文件的apk捎琐、jar会涎,前文我們提到dalvik不支持直接加載jar文件,那么為什么到了DexClassLoader這里怎么就可以支持加載了呢瑞凑?原因在于其父類BaseDexClassLoader對(duì)于“.jar”末秃,“.apk”,".zip"籽御,".dex"后綴的文件都會(huì)進(jìn)行對(duì)應(yīng)的處理练慕,最終提取成可執(zhí)行的dex文件。然而URLClassLoader并未對(duì)此做類似的處理技掏,因此我們一般會(huì)采用DexClassLoader做動(dòng)態(tài)加載铃将。

InMemoryDexClassLoader

 public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {
        super(dexBuffers, parent);
    }
   
    public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) {
        this(new ByteBuffer[] { dexBuffer }, parent);
    }

InMemoryDexClassLoader繼承于BaseDexClassLoader,是API26新增的類加載器哑梳。dexBuffers數(shù)組構(gòu)造了一個(gè)DexPathList劲阎,可用于加載內(nèi)存中的dex。

DelegateLastClassLoader

public DelegateLastClassLoader(String dexPath, ClassLoader parent) {
       super(dexPath, parent);
   }

DelegateLastClassLoader是API27新增的類加載器鸠真,繼承自 PathClassLoader哪工。DelegateLastClassLoader實(shí)行最后的查找策略。使用DelegateLastClassLoader來加載每個(gè)類和資源弧哎,使用的是以下順序:

  • 判斷是否已經(jīng)加載過該類
  • 搜索此類的類加載器是否已經(jīng)加載過該類
  • 搜索與此類加載器相關(guān)聯(lián)的dexPath文件列表雁比,并委托給父加載器。

雙親委托機(jī)制

Android類加載器通過loadClass加載目標(biāo)類撤嫩,下面是加載的源碼

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // 首先檢查當(dāng)前目標(biāo)類是否已經(jīng)被加載過偎捎,有則直接返回
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        //如果有父類加載器,優(yōu)先使用父類加載器尋找目標(biāo)類
                        c = parent.loadClass(name, false);
                    } else {
                         //其次序攘,如果有輔助類加載器茴她,使用輔助類加載器尋找目標(biāo)類
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    //如果仍未找到,則通過尋找子ClassLoader的目標(biāo)類(如果子ClassLoader重寫了findClass)
                    c = findClass(name);
                }
            }
            return c;
    }

由上述源碼程奠,我們可以總結(jié):

  • 當(dāng)前類加載器首先檢查目標(biāo)類是否已經(jīng)被加載過丈牢,有則直接返回
  • 當(dāng)前類加載器會(huì)先委托父類加載器加載目標(biāo)類,如果未設(shè)置父加載器瞄沙,則檢查輔助加載器是否支持查詢加載目標(biāo)類
  • 只有上述加載器找不到目標(biāo)類的時(shí)候己沛,才會(huì)調(diào)用當(dāng)前類加載器(Child) 查詢路徑尋找目標(biāo)類慌核。

以上這么做的好處是:一方面防止目標(biāo)類的重復(fù)加載,另外一方面 主要考慮安全因素申尼,防止有人重寫原生類垮卓,比如說java.lang.String這樣的數(shù)據(jù)類型,替換原生的String類师幕,加載到JVM中粟按,造成嚴(yán)重的安全問題。

雙親委托機(jī)制 在Android熱修復(fù)領(lǐng)域中也有著廣泛的應(yīng)用霹粥。每個(gè)ClassLoader可以有多個(gè)dex文件灭将,每個(gè)dex文件是一個(gè)Element,多個(gè)dex文件組成一個(gè)dexElements后控,類加載器尋找類的時(shí)候庙曙,會(huì)遍歷dexElements中的dex文件,再通過dex文件遍歷目標(biāo)類忆蚀。由于雙親委托機(jī)制的存在矾利,尋找到目標(biāo)類后就直接返回姑裂,不再尋找其他dex文件下該目標(biāo)類馋袜,熱修復(fù)的原理就是hook住ClassLoader,使其先加載修復(fù)后的目標(biāo)類舶斧,而存在的BUG的目標(biāo)類不會(huì)被加載欣鳖。

參考資料

  1. android動(dòng)態(tài)加載ClassLoader機(jī)制
  2. GoogleSource
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茴厉,隨后出現(xiàn)的幾起案子泽台,更是在濱河造成了極大的恐慌,老刑警劉巖矾缓,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀酷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嗜闻,警方通過查閱死者的電腦和手機(jī)蜕依,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琉雳,“玉大人样眠,你說我怎么就攤上這事〈渲猓” “怎么了檐束?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)束倍。 經(jīng)常有香客問我被丧,道長(zhǎng)盟戏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任晚碾,我火速辦了婚禮抓半,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘格嘁。我一直安慰自己笛求,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布糕簿。 她就那樣靜靜地躺著探入,像睡著了一般。 火紅的嫁衣襯著肌膚如雪懂诗。 梳的紋絲不亂的頭發(fā)上蜂嗽,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音殃恒,去河邊找鬼植旧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛离唐,可吹牛的內(nèi)容都是我干的病附。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亥鬓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼完沪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嵌戈,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤覆积,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后熟呛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宽档,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年庵朝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吗冤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偿短,死狀恐怖欣孤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昔逗,我是刑警寧澤降传,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站勾怒,受9級(jí)特大地震影響婆排,放射性物質(zhì)發(fā)生泄漏声旺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一段只、第九天 我趴在偏房一處隱蔽的房頂上張望腮猖。 院中可真熱鬧,春花似錦赞枕、人聲如沸澈缺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姐赡。三九已至,卻和暖如春柠掂,著一層夾襖步出監(jiān)牢的瞬間项滑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工涯贞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枪狂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓宋渔,卻偏偏與公主長(zhǎng)得像州疾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傻谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容