Too many methods in main-dex 是什么問題?

背景:

剛剛通過MultiDex 解決了Android 65535 的坑,怎么又出現(xiàn)了這個(gè)問題? MultiDex 是官方的解決方案難道還有問題?

先來說下MultiDex 是怎么回事

分兩步:

1.修改gradle腳本來產(chǎn)生多dex。

2.修改manifest 使用MulitDexApplication。

步驟1.在gradle腳本里寫上:

android {

compileSdkVersion 21

buildToolsVersion "21.1.0"

defaultConfig {

...

minSdkVersion 14

targetSdkVersion 21

...

// Enabling multidex support.

multiDexEnabled true

}

...

}

dependencies {

compile 'com.android.support:multidex:1.0.0'

}

步驟2. manifest聲明修改為MultiDexApplication

如果有自己的Application,繼承MulitDexApplication。如果當(dāng)前代碼已經(jīng)繼承自其它Application沒辦法修改那也行,就重寫 Application的attachBaseContext()這個(gè)方法。

@Override

protected void attachBaseContext(Context base) {

super.attachBaseContext(base);

MultiDex.install(this);

}

run一下,可以了!但是dex過程好像變慢了。。。

文檔還寫明了multiDex support lib 的局限。瞄一下是什么:

1.在應(yīng)用安裝到手機(jī)上的時(shí)候dex文件的安裝是復(fù)雜的(complex)有可能會因?yàn)榈诙€(gè)dex文件太大導(dǎo)致ANR。請用proguard優(yōu)化你的代碼。呵呵

2.使用了mulitDex的App有可能在4.0(api level 14)以前的機(jī)器上無法啟動,因?yàn)镈alvik linearAlloc bug(Issue 22586) 。請多多測試自祈多福。用proguard優(yōu)化你的代碼將減少該bug幾率。呵呵

3.使用了mulitDex的App在runtime期間有可能因?yàn)镈alvik linearAlloc limit (Issue 78035) Crash。該內(nèi)存分配限制在 4.0版本被增大,但是5.0以下的機(jī)器上的Apps依然會存在這個(gè)限制。

4.主dex被dalvik虛擬機(jī)執(zhí)行時(shí)候,哪些類必須在主dex文件里面這個(gè)問題比較復(fù)雜。build tools 可以搞定這個(gè)問題。但是如果你代碼存在反射和native的調(diào)用也不保證100%正確。呵呵

感覺這就是個(gè)坑啊。補(bǔ)丁方案又引入一些問題。但是插件化方案要求對現(xiàn)有代碼有比較大的改動,代價(jià)太大,而且動態(tài)化加載框架意味著維護(hù)成本更高,會有更多潛在bug。所以先測試,遇到有問題的版本再解決。

4.0 系統(tǒng) ANR 了 ?

問題又來了!這次不僅僅是2.3 的機(jī)型!還有一些中檔配置的4.x系統(tǒng)的機(jī)型。問題現(xiàn)象是:第一次安裝后,點(diǎn)擊圖標(biāo),1s,2s,3s... 程序沒有任何反應(yīng)就好像你沒點(diǎn)圖標(biāo)一樣。

5s過去。。。程序ANR!

其實(shí)不僅僅總悟君的App存在這個(gè)問題,其他很多App也存在首次安裝運(yùn)行后幾秒都無任何響應(yīng)的現(xiàn)象或者最后ANR了。唯一的例外是美團(tuán)App,點(diǎn)擊圖標(biāo)立馬就出現(xiàn)界面。唉要不就算啦?反正就一次。。。不行,這可是產(chǎn)品給用戶的第一印象啊太重要了,而且美團(tuán)搞得定就說明這問題有解決方案。

ANR了是不是局限1描述的現(xiàn)象??不過也不重要...因?yàn)镚oogle只是告訴你說第二個(gè)dex太大了導(dǎo)致的。并沒有進(jìn)一步解釋根本原因。怎么辦?Google一發(fā)?搜索點(diǎn)擊圖標(biāo) 然后ANR?怎么可能有解決方案嘛。ANR就意味著UI線程被阻塞了,老老實(shí)實(shí)查看log吧。

adb logcat -v time > log.txt

于是發(fā)現(xiàn) 是 install dex + dexopt 時(shí)間太長!

梳理一下流程:

安裝完app點(diǎn)擊圖標(biāo)之后,系統(tǒng)木有發(fā)現(xiàn)對應(yīng)的process,于是從該apk抽取classes.dex(主dex) 加載,觸發(fā) 一次dexopt。

App 的laucherActivity準(zhǔn)備啟動 ,觸發(fā)Application啟動,

Application的 onattach()方法調(diào)用,這時(shí)候MultiDex.install()調(diào)用,classes2.dex 被install,再次觸發(fā)dexopt。

然后Applicaition onCreate()執(zhí)行。

然后 launcher Activity真的起來了。

這些必須在5s內(nèi)完成不然就ANR給你看!

有點(diǎn)棘手。首先主dex是無論如何都繞不過加載和dexopt的。如果主dex比較小的話可以節(jié)省時(shí)間。主dex小就意味著后面的dex大啊,MultiDex.install()是在主線程里做的,總時(shí)間又沒有實(shí)質(zhì)性改變。install() 能不能放到線程里做啊?貌似不行。。。如果異步化,什么時(shí)候install完成都不知道。這時(shí)候如果進(jìn)程需要seconday.dex里的classes信息不就悲劇?主dex越小這個(gè)錯(cuò)誤幾率就越大。要悲劇啊總悟君。

淡定,這次Google搜索MultiDex.install 。于是總悟君發(fā)現(xiàn)了美團(tuán)多dex拆包方案。 讀完之后感覺看到勝利曙光。美團(tuán)的主要思路是:精簡主dex+異步加載secondary.dex 。對異步化執(zhí)行速度的不確定性,他們的解決方案是重寫Instrumentation execStartActivity 方法,hook跳轉(zhuǎn)Activity的總?cè)肟谧雠袛啵绻?dāng)前secondary.dex 還沒有加載完成,就彈一個(gè)loading Activity等待加載完成,如果已經(jīng)加載完成那最好不過了。不錯(cuò),RTFSC果然是王道。 可以試一試。

但是有幾個(gè)問題需要解決:

1.分析主dex需要的classes這個(gè)腳本比較難寫。。。Google文檔說過這個(gè)問題比較復(fù)雜, 而且buildTools 不是已經(jīng)幫我們搞定了嗎?去瞄一下主dex的大小:8M 以及secondary.dex 3M 。 它是如何工作的?文檔說dx的時(shí)候,先依據(jù)manifest里注冊的組件生成一個(gè) main-list,然后把這list里的classes所依賴的classes找出來,把他們打成classes.dex就是主dex。剩下的classes都放clsses2.dex(如果使用參數(shù)限制dex大小的話可能會有classe3.ex 等等) 。主dex至少含有main-list 的classes + 直接依賴classes ,使用mini-main-list參數(shù)可以僅僅包含剛才說的classes。

關(guān)于寫分析腳本的思路是:直接使用mini-main-list參數(shù)獲取build目錄下的main-list文件,這樣manifest聲明的類和他們的直接依賴類搞定的了,那后者的直接依賴類怎么解?這些在dvk runtime也是必須的classes。一個(gè)思路是解析class文件獲得該class的依賴類。還一個(gè)思路是自己使用Dexclassloader 加載dex,然后hook getClass()方法,調(diào)用一次就記錄一個(gè)。都挺折騰的。

2.由于歷史原因,總悟君在維護(hù)的App的manifest注冊的組件的那些類,承載業(yè)務(wù)太多,依賴很多三方j(luò)ar,導(dǎo)致直接依賴類非常多,而且短時(shí)間內(nèi)無法梳理精簡,沒辦法mini化主dex。

3.Application的啟動入口太多。Appication初始化未必是由launcher Activity的啟動觸發(fā),還有可能是因?yàn)镾ervice ,Receiver ,ContentProvider 的啟動。 靠攔截重寫Instrumentation execStartActivity 解決不了問題。要為 Service ,Receiver ,ContentProvider 分別寫基類,然后在oncreate()里判斷是否要異步加載secondary.dex。如果需要,彈出Loading Acitvity?用戶看到這個(gè)會感覺比較怪異。

結(jié)合自身App的實(shí)際情況來看美團(tuán)的拆包方案雖然很美好然但是不能照搬啊。果然不能愉快地回家看動漫了。

相關(guān)資料

Android拆分與加載Dex的多種方案對比

美團(tuán)Android DEX自動拆包及動態(tài)加載簡介

Android傻瓜式分包插件及一些坑

MultiDex中出現(xiàn)的main dex capacity exceeded解決之道

Android Dex分包之旅

dex分包變形記









...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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