Android動(dòng)態(tài)加載學(xué)習(xí)總結(jié)(一):類加載器

本文取自本人CSDN的博客:
http://blog.csdn.net/gaozhan_csdn/article/details/52085911
話說(shuō)簡(jiǎn)書(shū)不是很適合代碼編寫啊妻往,好麻煩几迄。

參考資料: 《深入理解Java虛擬機(jī)》 -周志明 Android中的動(dòng)態(tài)加載機(jī)制
本篇不深入涉及Java類加載器,如果想更深入了解正罢,可以看一下這篇博客http://blog.csdn.net/zhoudaxia/article/details/35824249
前言:動(dòng)態(tài)加載在應(yīng)用開(kāi)發(fā)中有著很重要的地位,當(dāng)我們項(xiàng)目越來(lái)越大驻民,我們可以通過(guò)插件化來(lái)減少應(yīng)用的內(nèi)存翻具,然后動(dòng)態(tài)加載那些插件。還有一個(gè)方面回还,如果我們的應(yīng)用頻繁的更新裆泳,頻繁的發(fā)布新版本,肯定會(huì)造成用戶體驗(yàn)下降柠硕,那么我們可以用動(dòng)態(tài)加載技術(shù)在不發(fā)布新版本的情況下更新一些模塊工禾。
那么既然要用動(dòng)態(tài)加載,就肯定涉及到類加載器蝗柔,我們先看一下Java中的類加載器闻葵。
一、Java中類加載器
在這里癣丧,我們不去說(shuō)類加載的具體過(guò)程笙隙,只是總結(jié)一下Java中類加載器與雙親委派模型。

1.類加載器與類本身確定類的唯一性
對(duì)于一個(gè)類坎缭,這個(gè)類本身和加載它的類加載器共同確定其在虛擬機(jī)中的唯一性竟痰。 我們使用兩個(gè)類加載器進(jìn)行加載同一個(gè)類,那么這兩個(gè)類是不相等的掏呼,那么虛擬機(jī)中會(huì)存在兩個(gè)同名的類坏快。
2.Java三種預(yù)定義類型類加載器

  • 啟動(dòng)類加載器(Bootstrap ClassLoader,也稱為引導(dǎo)類加載器)
    該類加載器負(fù)責(zé)將存放在\lib目錄中憎夷,或者被-Xbootclasspath參數(shù)所指定的路徑中的莽鸿,并且是虛擬機(jī)識(shí)別的(這點(diǎn)很重要)類加載到虛擬機(jī)中。
    加載虛擬機(jī)識(shí)別的這一點(diǎn)與雙親委派模型配合很重要。在下面介紹祥得,先留意這一點(diǎn)兔沃。
    對(duì)于啟動(dòng)類加載器還有一點(diǎn)需要注意,也是和雙親委派模型有關(guān)级及,如果我們自定義一個(gè)類加載器乒疏,想把一個(gè)加載請(qǐng)求委派給啟動(dòng)類加載器,只需要使用null替代即可(可以看下面的findClass()方法中的代碼實(shí)現(xiàn))饮焦。
    開(kāi)發(fā)者不可以直接使用該加載器怕吴。

  • 擴(kuò)展類加載器(Extension ClassLoader)
    該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),負(fù)責(zé)加載\lib\ext目錄中的县踢,或者被java.ext.dirs系統(tǒng)變量所指定的路程中的所有類庫(kù)转绷。
    開(kāi)發(fā)者可以直接使用該加載器。

  • 應(yīng)用程序類加載器(Application ClassLoader硼啤,也稱為系統(tǒng)類加載器)
    該類加載器由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)议经。由于這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也稱為系統(tǒng)類加載器谴返。該加載器負(fù)責(zé)加載用戶類路徑(ClassPath)上所指定的類庫(kù)煞肾。
    開(kāi)發(fā)者可以直接使用這個(gè)類加載器。如果應(yīng)用程序中沒(méi)有自定義過(guò)自己的類加載器亏镰,一般情況下該加載器是程序中的默認(rèn)的類加載器扯旷。

3.雙親委派模型


這里寫圖片描述

上圖顯示了類加載器之間的層次關(guān)系,被稱為類加載器的雙親委派模型索抓。

除了頂層的啟動(dòng)類加載器外钧忽,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。

那么根據(jù)上圖逼肯,如果一個(gè)類加載器收到了類加載的請(qǐng)求耸黑,它首先不會(huì)自己嘗試加載這個(gè)類,而是把請(qǐng)求委派給父類加載器去加載篮幢,每一個(gè)層次的類加載器都是這樣大刊,那么所有的請(qǐng)求其實(shí)最后都是會(huì)到啟動(dòng)類加載器中,如果父類加載器反饋無(wú)法加載三椿,子加載器才會(huì)嘗試自己加載缺菌。

實(shí)現(xiàn)雙親委派模型的代碼在loadClass()方法中:

jianshu1.PNG

4.雙親委派模型的優(yōu)點(diǎn)

大家都知道Object類是個(gè)基礎(chǔ)類,如果我們自己寫了一個(gè)Object類搜锰,那么如果沒(méi)有雙親委派模型的話伴郁,再加上我們并沒(méi)有用啟動(dòng)類加載器去加載我們寫的這個(gè)Object類的話,系統(tǒng)中會(huì)存在兩個(gè)Object類(參考上述的類在虛擬機(jī)中的唯一性)蛋叼。那么有了雙親委派模型焊傅,我們寫了一個(gè)Object類剂陡,會(huì)先去檢查它是否加載了(肯定已經(jīng)加載了),那么我們寫的這個(gè)就不會(huì)被重復(fù)加載狐胎,也就保證了基礎(chǔ)類的唯一性鸭栖,防止混亂破壞。就算沒(méi)有檢查握巢,根據(jù)上面關(guān)于啟動(dòng)類加載器的介紹晕鹊,必須是虛擬機(jī)識(shí)別的,Object存放在rt.jar中镜粤,我們寫的不會(huì)被識(shí)別捏题。

那么從另一個(gè)方面玻褪,基礎(chǔ)類Object怎么保證的在任何環(huán)境下都是同一個(gè)類(即加載器在任何情況下都是同一個(gè))肉渴,這就是雙親委派模型的作用了,每次這個(gè)加載請(qǐng)求都會(huì)委派給處于最頂端的啟動(dòng)類加載器進(jìn)行加載带射,虛擬機(jī)識(shí)別rt.jar同规,那么就保證了每次都是由啟動(dòng)類加載器加載的Object,那么根據(jù)第1條的唯一性窟社,這樣做就保證了Object的唯一性券勺。

5.自定義類加載器符合雙親委派模型

我們根據(jù)上面的介紹知道,雙親委派模型的邏輯都在loadClass()方法中灿里,那么我們?yōu)榱瞬黄茐碾p親委派模型关炼,自定義類加載器時(shí)不去重寫loadClass()方法,而是重寫findClass()方法匣吊,將自己的類加載邏輯寫到findClass()方法中儒拂,在loadClass()方法中,最后父類加載器無(wú)法加載的時(shí)候色鸳,調(diào)用的就是findClass()方法社痛。這樣我們就保證了我們自定義的類加載器是符合雙親委派模型的。如果重寫loadClass()方法命雀,會(huì)出現(xiàn)一系列錯(cuò)誤蒜哀,比如基礎(chǔ)類加載不上等。

findClass()方法是在JDK1.2以后引入的吏砂。

5.defineClass()方法
//定義類型撵儿,一般在findClass方法中讀取到對(duì)應(yīng)字節(jié)碼后調(diào)用,final狐血,不可繼承淀歇,一般不用重寫,直接調(diào)用氛雪。在findClass()方法中調(diào)用房匆。
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{ … }

介紹這個(gè)是為了注明下面的Android類加載與Java中類加載的區(qū)別。

二、Android類加載器

前面介紹的是Java中的類加載器浴鸿,我們知道android中的虛擬機(jī)是Dalvik井氢,它不是標(biāo)準(zhǔn)的Java虛擬機(jī),所以在類加載機(jī)制上岳链,和Java中的類加載器有一些區(qū)別花竞。

我們?cè)趈ava標(biāo)準(zhǔn)的虛擬機(jī)中,如果自定義類加載器掸哑,會(huì)繼承ClassLoader约急,并重寫findClass()方法,在內(nèi)部調(diào)用defineClass()去從一個(gè)二進(jìn)制流中加載Class苗分。那么在Android中厌蔽,這個(gè)defineClass()方法去調(diào)用VMClassLoader的defineClass本地靜態(tài)方法,而這個(gè)方法內(nèi)部除了拋出了異乘ぱⅲ“UnsupportedOperationException”還有一些屬性值奴饮,其他什么都沒(méi)做。在博客開(kāi)頭寫的鏈接中有源碼择浊,這就不貼了戴卜。
那么在Dalvik虛擬中,動(dòng)態(tài)加載類就需要另外由ClassLoader派生出的兩個(gè)類:DexClassLoader和PathClassLoader琢岩。

這兩個(gè)類重載了ClassLoader的findClass()方法投剥,并沒(méi)有重寫loadClass()方法,所以這兩個(gè)類加載器符合雙親委派模型担孔。

在介紹這兩個(gè)類加載器區(qū)別之前江锨,說(shuō)明兩點(diǎn)
(1)Dalvik虛擬機(jī)識(shí)別的是dex文件,而不是class文件攒磨,因此泳桦,我們加載的是dex文件,或者包含dex文件的apk文件或jar文件娩缰。
(2)DexFile類灸撰。上述兩個(gè)類加載器都是通過(guò)DexFile這個(gè)類去加載類。

區(qū)別: PathClassLoader不能主動(dòng)從zip包中釋放出dex拼坎,所以只支持直接操作dex格式文件浮毯,或者已經(jīng)安裝的apk(已經(jīng)安裝的apk在cache中存在緩存的dex文件)。而DexClassLoader可以支持.apk泰鸡、.jar和.dex文件债蓝,并且會(huì)在指定的outpath路徑釋放出dex文件。
下面會(huì)有加載的demo盛龄。

加載好類以后饰迹,我們可以通過(guò)反射來(lái)使用加載好的類芳誓,但過(guò)多的使用反射會(huì)有一定的性能開(kāi)銷,代碼復(fù)雜凌亂啊鸭。我們還有一種方式锹淌,即接口。我們可以將一些方法提取出來(lái)作為一個(gè)接口赠制,將待加載的類實(shí)現(xiàn)這個(gè)接口赂摆,在寫待加載的類時(shí),注意要有一個(gè)參數(shù)為空的構(gòu)造函數(shù)钟些,我們?cè)谥鞔a中就能將類對(duì)象強(qiáng)制轉(zhuǎn)為接口對(duì)象烟号,直接調(diào)用成員方法。

三政恍、Demo

這個(gè)Demo是Android中的動(dòng)態(tài)加載機(jī)制里的汪拥,為了展示,簡(jiǎn)略了一下抚垃,并且由于android系統(tǒng)的變更喷楣,其中有一些小坑趟大,我們需要改一下代碼鹤树。

1.接口與待加載類的實(shí)現(xiàn)與處理


這里寫圖片描述
  • 接口代碼

public interface IDynamic {
void init(Activity var1);
//彈出消息
void showSomething();
void destory();
}

  • 待加載類
jianshu2.PNG
  • 接口的處理
    將接口導(dǎo)出一個(gè)jar包(由于AS導(dǎo)包得修改Gradle,嫌麻煩加上電腦上有Eclipse for Android 逊朽,我用它打的包):


    這里寫圖片描述

    然后將接口的jar包放入AS工程里的libs目錄下罕伯。

  • 待加載類的處理
    將待加載類按上述方式打包,將打好的jar包復(fù)制到SDK的build-tools目錄下叽讳,打開(kāi)命令行追他,進(jìn)入build-tools目錄,輸入:
    dx –dex –output dynamic1.apk dynamic.jar

    注意:上面dex和output前是兩個(gè)‘-’ 岛蚤,csdn顯示的是一個(gè)邑狸,只不過(guò)長(zhǎng)度長(zhǎng)一點(diǎn),坑了我一會(huì)涤妒。 如下圖:
    這里寫圖片描述

    經(jīng)過(guò)上述步驟单雾,build-tools目錄下就會(huì)生成一個(gè)名為dynamic1.apk的文件,那么上述步驟究竟做了什么她紫?

我們知道DexClassLoader和PathClassLoader這兩個(gè)類加載器加載的并不是class文件硅堆,而是將class文件優(yōu)化后的dex文件,上述步驟便是將jar包解壓贿讹,將其中的class文件優(yōu)化成dex文件渐逃,然后壓縮為apk文件。

  • 目標(biāo)類的實(shí)現(xiàn)與處理
3.PNG
4.PNG
  • AndroidManifest的處理

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.administrator.dynamicloading"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
android:supportsRtl="true" android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter> <action android:name="android.intent.action.MAIN"/>
//注意民褂,和目標(biāo)類中相對(duì)應(yīng)
<action android:name="com.example.impl"/>
<category android:name="android.intent.category.LAUNCHER"/> </intent-filter>
</activity>
</application>
</manifest>

  • 將dynamic1.apk放入相應(yīng)目錄

好了茄菊,處理的差不多了疯潭,該處理我們?cè)诖虞d類處理那一步中生成的dynamic1.apk了。因?yàn)橛肈exClassLoader按照Android中的動(dòng)態(tài)加載機(jī)制中的方法我并沒(méi)有成功面殖,因?yàn)椴┲魇菍ar包放進(jìn)SD卡中袁勺,現(xiàn)在好像并不支持這樣做,所以這次我打算用PathClassLoader畜普,看了上面介紹的小伙伴已經(jīng)知道期丰,PathClassLoader只能識(shí)別dex文件和已經(jīng)安裝好的apk文件(安裝好的會(huì)有相應(yīng)的緩存dex文件),那么我們接下來(lái)就要想辦法把咱們生成的apk文件放到一個(gè)目錄下吃挑,放在哪個(gè)目錄下呢钝荡?

一開(kāi)始我也不清楚,不過(guò)我將下面這個(gè)屬性用Toast顯示了一下舶衬,便得到了目錄 String apkPath = actInfo.applicationInfo.sourceDir;
要放的目錄是: /data/app/com.example.administrator.dynamicloadi ng-1

這里需要說(shuō)明一點(diǎn)埠通,如果你用的是真機(jī)的話,可能需要root權(quán)限才能進(jìn)去這些目錄逛犹,我用的模擬器端辱,不需要權(quán)限。

還有一個(gè)坑虽画,這個(gè)項(xiàng)目需要先運(yùn)行一下舞蔽,app目錄下才會(huì)生成com.example.administrator.dynamicloading-1這個(gè)文件(當(dāng)然你生成的不一定是這個(gè),取決于的你的包名)

注意:下面的一些命令都有一個(gè)前提码撰,需要開(kāi)啟模擬器渗柿。或者連著真機(jī)脖岛。

項(xiàng)目運(yùn)行完后朵栖,你可以使用以下命令看看有沒(méi)有相應(yīng)包名的文件:

這里寫圖片描述

adb shell進(jìn)入模擬器,然后cd /data/app進(jìn)入app目錄柴梆,然后ls命令查看一下該目錄中都有哪些文件陨溅。 我要進(jìn)入的目錄如下(因?yàn)樯厦婺莻€(gè)屬性Toast顯示的是它):
這里寫圖片描述

好了,那么用命令行將apk文件放入這個(gè)目錄绍在,先使用exit命令退出模擬器门扇。
接下來(lái),用命令將dynamic1.apk放入這個(gè)目錄 命令如下: adb push apk所在目錄 要放入的目錄
例如我的apk在剛才的build-tools目錄下揣苏,要放進(jìn)/data/app/com.example.administrator.dynamicloading-1悯嗓,所以命令如下圖:

這里寫圖片描述

如果想查看有沒(méi)有復(fù)制成功,可以按照上述的方法檢查一下卸察,adb shell 脯厨,然后進(jìn)入這個(gè)目錄,ls一下坑质。

好了合武,運(yùn)行項(xiàng)目临梗,看看結(jié)果:
這里寫圖片描述

成功加載了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稼跳,一起剝皮案震驚了整個(gè)濱河市盟庞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汤善,老刑警劉巖什猖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異红淡,居然都是意外死亡不狮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門在旱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摇零,“玉大人,你說(shuō)我怎么就攤上這事桶蝎∽そ觯” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵登渣,是天一觀的道長(zhǎng)噪服。 經(jīng)常有香客問(wèn)我,道長(zhǎng)绍豁,這世上最難降的妖魔是什么芯咧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮竹揍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邪铲。我一直安慰自己芬位,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布带到。 她就那樣靜靜地躺著昧碉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揽惹。 梳的紋絲不亂的頭發(fā)上被饿,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音搪搏,去河邊找鬼狭握。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疯溺,可吹牛的內(nèi)容都是我干的论颅。 我是一名探鬼主播哎垦,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼恃疯!你這毒婦竟也來(lái)了漏设?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤今妄,失蹤者是張志新(化名)和其女友劉穎郑口,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盾鳞,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潘酗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雁仲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔夺。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖攒砖,靈堂內(nèi)的尸體忽然破棺而出缸兔,到底是詐尸還是另有隱情,我是刑警寧澤吹艇,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布惰蜜,位于F島的核電站,受9級(jí)特大地震影響受神,放射性物質(zhì)發(fā)生泄漏抛猖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一鼻听、第九天 我趴在偏房一處隱蔽的房頂上張望财著。 院中可真熱鬧,春花似錦撑碴、人聲如沸撑教。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伟姐。三九已至,卻和暖如春亿卤,著一層夾襖步出監(jiān)牢的瞬間愤兵,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工排吴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秆乳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓傍念,卻偏偏與公主長(zhǎng)得像矫夷,于是被迫代替她去往敵國(guó)和親葛闷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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