dex分包方案概述與multidex包的配置使用

c說(shuō)明:該篇取自我CSDN的博客http://blog.csdn.net/gaozhan_csdn/article/details/51992100

參考資料:

Android dex分包方案

Android分包MultiDex原理

《Android開(kāi)發(fā)藝術(shù)探索》

博客中間會(huì)涉及到dex文件的反編譯,參考博文:

dex文件的反編譯-dex2jar和jd-gui

1.dex分包的原因

對(duì)于功能越來(lái)越復(fù)雜的app的兩大問(wèn)題

問(wèn)題一:當(dāng)項(xiàng)目越來(lái)越大,方法數(shù)超過(guò)65536,編譯時(shí)會(huì)出錯(cuò)(為什么是65536,參考下面關(guān)于dexopt對(duì)方法id檢索存儲(chǔ)介紹),這個(gè)所說(shuō)的方法數(shù)包含用到的框架,依賴的jar包,當(dāng)然還有我們應(yīng)用本身的代碼中的所有方法(我們自己寫(xiě)的)。

我們可以寫(xiě)個(gè)Demo看看報(bào)的具體錯(cuò)誤。

那我們寫(xiě)個(gè)65536以上個(gè)方法,可以用java的IO流向一個(gè)txt里寫(xiě)入65537個(gè)方法。

public class MethodWriter {

public static void main(String[] args) throws IOException{?

?? FileWriter fw =new FileWriter("demo.txt");

for(int i =1; i <=65537; i++){? ??

? ? fw.write("public void me"+ i +"(){ }\r\n");

? ? fw.flush();

? ? }

? ? fw.close();

}

}


然后復(fù)制txt文件里的方法到AS工程里即可。注意,將這些方法分別放在幾個(gè)類下面,保證每個(gè)類不要超過(guò)65536。我們所說(shuō)的65536限制是整個(gè)項(xiàng)目的限制。下面,我們分兩種方案放置這些方法,運(yùn)行項(xiàng)目,看看AS會(huì)有什么結(jié)果。

方案一:我們自己應(yīng)用的方法數(shù)超過(guò)了65536

我們所說(shuō)的方法數(shù)限制,這個(gè)方法數(shù)包括了jar包,框架,還有我們自己應(yīng)用的代碼,當(dāng)我們應(yīng)用的代碼超過(guò)65536時(shí),結(jié)果如下:

我們看到,顯示我們方法的引用是65579.而引用數(shù)最大是65536,建議我們開(kāi)啟分包方案。

方案二:我們應(yīng)用的方法數(shù)沒(méi)有超過(guò)65536,但是加上依賴的jar包,框架等,超過(guò)了65536(根據(jù)方案一的結(jié)果,我們應(yīng)用方法數(shù)是65579,那我們刪掉200個(gè)方法,就小于65536)

報(bào)錯(cuò)如下:

問(wèn)題二:方法數(shù)并沒(méi)有超過(guò)65536,編譯也完成了,但是在android2.3以前的系統(tǒng)安裝的時(shí)候,會(huì)異常中止安裝。

這個(gè)問(wèn)題會(huì)發(fā)生在Android 2.2以及Android 2.3的設(shè)備上,涉及到一個(gè)名為dexopt的程序,全稱dex optimization,即dex文件優(yōu)化程序。在優(yōu)化過(guò)程中,dexopt采用一個(gè)固定大小的緩沖區(qū)(LinearAlloc)來(lái)存儲(chǔ)應(yīng)用中所有方法的信息,那么之所以會(huì)出現(xiàn)在老版本停止安裝,是因?yàn)槔习姹镜木彌_區(qū)的大小是5M,而在新版本中,這個(gè)緩沖區(qū)的大小是8M或者16M,在老版本中更容易超過(guò)這個(gè)限制。

dexopt的執(zhí)行過(guò)程是在第一次加載dex文件的時(shí)候執(zhí)行的。這個(gè)過(guò)程產(chǎn)生了一個(gè)ODEX文件,全稱Optimised Dex。這個(gè)ODEX文件是在安裝過(guò)程中從apk里提取出的可運(yùn)行文件,是優(yōu)化dex產(chǎn)生的,再把a(bǔ)pk包里的dex文件刪除,這樣就做到了預(yù)先提取。如果沒(méi)有ODEX文件,那么系統(tǒng)會(huì)從apk包中提取dex然后再運(yùn)行。所以優(yōu)化后可以加快軟件的啟動(dòng)速度,預(yù)先提取,減少對(duì)RAM的占用。

在早期的Android系統(tǒng)中,dexopt會(huì)把每一個(gè)類的方法id檢索起來(lái),存在一個(gè)鏈表結(jié)構(gòu)里,而這個(gè)鏈表的長(zhǎng)度是用一個(gè)short類型來(lái)保存的,導(dǎo)致方法id的數(shù)目不能夠超過(guò)65536個(gè)。雖然新版本的android系統(tǒng)中,dexopt修復(fù)了這個(gè)問(wèn)題,但是老版本的android系統(tǒng)的用戶市場(chǎng)占有率還是占一定比例,還是不能放棄這部分用戶的,所以我們?cè)陂_(kāi)發(fā)中需要對(duì)老版本的這個(gè)問(wèn)題進(jìn)行兼容。

2.方法數(shù)越界的解決方案

插件化技術(shù)

我們可以采用動(dòng)態(tài)加載部分dex,通過(guò)將一個(gè)dex拆分成兩個(gè)或多個(gè)dex,解決方法數(shù)越界的問(wèn)題。

插件化是一套重量級(jí)的技術(shù)方案,我們需要通過(guò)反射來(lái)調(diào)用插件的類或方法,要使用一套插件框架來(lái)配合,而且插件化適合一些獨(dú)立的模塊,兼容性問(wèn)題往往較多,如果只是用于解決方法數(shù)越界的話,并不是最好的方案。

multidex解決方案

為了解決方法數(shù)越界的問(wèn)題,Google在2014年提出了multidex的解決方案,這個(gè)方案主要是針對(duì)AndroidStudio和Gradle編譯環(huán)境的,將一個(gè)dex文件拆成兩個(gè)或多個(gè)dex文件。

不過(guò)需要注意的是multidex有一個(gè)版本問(wèn)題,在Android 5.0以前使用multidex需要手動(dòng)引入Google提供的android-support-multidex.jar這個(gè)jar包。這個(gè)jar包我們可以在Android SDK目錄下的extras/android/support/multidex/library/libs下找到。而從Android 5.0開(kāi)始,Andorid默認(rèn)支持了multidex。

所以,我們就需要注意我們的SDK版本了,如果已經(jīng)支持了multidex,而我們又把a(bǔ)ndroid-support-multidex.jar放在了項(xiàng)目的libs文件下,就會(huì)報(bào)錯(cuò)。

3.在Gradle和代碼中配置使用Multidex

在Gradle中配置使用Multidex

由于Android的Gradle插件在Android Build Tool 21.1開(kāi)始支持使用multidex,所以我們需要使用Android Build Tools 21.1及以上版本,修改app目錄下的build.gradle文件,有兩點(diǎn)需要修改。

(1)在defaultConfig中添加multiDexEnabled true這個(gè)配置項(xiàng)。

(2)在dependencies中添加multidex的依賴:

compile ‘com.android.support:multidex:1.0.0’

注意buildToolsVersion要高于21.1,配置好如下:

在Gradle中配置好之后,我們還需要在代碼中加入支持multidex的功能,有三種方案可選

方案一:在manifest文件中指定Application為MultiDexApplication,如下:

方案二:寫(xiě)一個(gè)Application類并繼承MultiDexApplication,并在AndroidManifest.xml的application標(biāo)簽中進(jìn)行注冊(cè)(在application標(biāo)簽中增加name屬性,并添加自己的Application類名即可),如果不是想重寫(xiě)MultiDexApplication中一些方法的話,還是方案一更方便些。如下:

注冊(cè)如下:

方案三:如果不想按方案二繼承,我們可以重寫(xiě)Application的attachBaseContext方法,注意,這個(gè)方法比onCreate方法先執(zhí)行。具體方法是創(chuàng)建一個(gè)新類,繼承Application,然后重寫(xiě)attachBaseContext方法,并在AndroidManifest.xml的application標(biāo)簽中進(jìn)行注冊(cè)(與方案二注冊(cè)相同)如下:

對(duì)于在AndroidManifest.xml中注冊(cè),與方案二的注冊(cè)相同。

3.使用Multide分包后兩種情況的結(jié)果

我們的Demo圖如下,我們根據(jù)該圖和dex文件反編譯的結(jié)果分析分包情況。

情況一:方法數(shù)沒(méi)有越界

我們將方法數(shù)控制在65536以內(nèi),方法數(shù)沒(méi)有越界的話,是不會(huì)分包的,解壓apk,你會(huì)發(fā)現(xiàn)apk里只有一個(gè)classes.dex,如下

將其反編譯后(不知道怎么反編譯的可以看一下這篇博文:http://blog.csdn.net/gaozhan_csdn/article/details/51984056),結(jié)果如下:

可以發(fā)現(xiàn),我們的類都在這個(gè)主dex文件里,并沒(méi)有分包。

情況二:方法數(shù)越界

我們?cè)賹⒎椒〝?shù)增加到65536以上。解壓apk,結(jié)果如下:

對(duì)三個(gè)dex文件反編譯一下,看看它們里面分別都包含了什么類。

classes.dex(主dex)下的類視圖:

classes2.dex的類視圖

classes3.dex的類視圖

可以發(fā)現(xiàn)Second類和Third類分別在classes2.dex文件和classes3.dex文件里,其他類都在主dex文件里(classes.dex),我們用multidex的確實(shí)現(xiàn)了分包從而解決了方法數(shù)越界的問(wèn)題。

4.使用MultiDex存在的一些問(wèn)題

MultiDex使用起來(lái)很方便,但它也有一定的局限性。先總結(jié)一下網(wǎng)上查到的,后續(xù)研究。

由于需要加載額外的dex文件,應(yīng)用的啟動(dòng)速度會(huì)降低,當(dāng)其他dex文件較大的時(shí)候,甚至?xí)霈F(xiàn)ANR現(xiàn)象。

由于Dalvik linearAlloc的Bug,有可能導(dǎo)致使用multidex的應(yīng)用無(wú)法在Android4.0以前的手機(jī)上運(yùn)行。也是這個(gè)bug,可能出現(xiàn)應(yīng)用在運(yùn)行中由于采用了multidex方案從而產(chǎn)生大量?jī)?nèi)存消耗的情況,導(dǎo)致應(yīng)用崩潰。

小結(jié)與后續(xù):

multidex涉及版本問(wèn)題,所以使用的時(shí)候一定要注意,不同版本該不該手動(dòng)導(dǎo)包

分包后,由于dex的加載問(wèn)題,可能會(huì)出現(xiàn)找不到類的問(wèn)題,所以我們需要研究一下如何將某個(gè)具體的類按我們的意愿放入到某個(gè)dex里

multidex源碼

dex如何加載,加載機(jī)制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容