Android Gradle和Ant型別分包

2021-07-15 05:27:40 字數 3819 閱讀 6739

作為一名android開發者,相信你對android方法數不能超過65k的限制應該有所耳聞,隨著應用程式功能不斷的豐富,總有一天你會遇到乙個異常:

conversion to dalvik format failed:unable toexecute dex: method id not in [0, 0xffff]: 65536

可能有些同學會說,解決這個問題很簡單,我們只需要在project.proterty中配置一句話就ok啦,

dex.force.jumbo=true

是的,加入了這句話,確實可以讓你的應用通過編譯,但是在一些2.3系統的機器上很容易出現

install_failed_dexopt異常

對於以上兩個異常,我們先來分析一下原因:

1、android系統中,乙個dex檔案中儲存方法id用的是short型別資料,所以導致你的dex中方法不能超過65k

2、在2.3系統之前,虛擬機器記憶體只分配了5m

知道了原因,我們就來乙個個的解決上面的問題,首先對於65k的問題,我們在應用層是無法改變android系統的結構的,所以我們無法將資料型別從short改變為int或者其他型別,也就是說乙個dex中的方法數不能超過65k是我們無法逾越的鴻溝,我們只能減少乙個dex中的方法數,首先最容易想到的方案就是去掉一些無用的jar包,以及將一些屬性設定為public,從而可以去掉get/set方法,這種方法只能臨時解決問題,隨著時間的推移,總有一天還是會出現方法數超過65k的,畢竟乙個應用一般是在加功能,不會減功能。

關於外掛程式開發,做成動態載入,我在很早之前一篇文章中就寫過其基本思想,有興趣的同學可以看看

《實現android 動態載入apk(fragment or activity實現)》

下面我們重點介紹分包機制

我們知道乙個apk檔案裡面有乙個dex檔案,這個dex檔案裡面都是經過優化了的class檔案,所謂分包,就是講乙個dex檔案分成多個dex檔案,這裡我們約定一下,第乙個dex叫做main.dex,第二個叫做second.dex,通常在分包的時候,我們需要將應用啟動就需要使用的類放入到main.dex中,把不是立馬就需要使用的類放入到second.dex中,對於android系統,他只會預設載入main.dex的,second.dex對於他來說可能只是乙個資源檔案,它是不會主動去載入second.dex,所以我在應用啟動的過程中,我們需要為second.dex建立好乙個類載入器,便於我在使用second.dex中的類時,能夠裡面載入該類。

關於如何載入second.dex也有好多做法,用的比較多的主要有一下幾種

1、最簡單的做法就是使用dexclassloader進行載入,並將該dexclassloader的父載入器設定為pathclassloader

2、使用dexclassloader載入,並將dexclassloader的父載入器設定成pathclassloader的父載入器,將pahtclassloader的父載入器設定成dexclassloader,仔細品味一下1和2的區別

3、將second.dex的路徑放入到pathclassloader的載入路徑中

對於第2中方案,在有一種情況下是不能使用的,比如當second.dex通過dexclassloader載入,但是second.dex中使用了乙個類,這個類在main.dex中,這個時候就會丟擲類找不到的異常,所以這種方案只能擁有second.dex不會用到main.dex類的時候

以上說的都是理論,下面我們來實戰一下

我這裡會介紹兩種方案,一種是基於gradle構建android專案,一種是基於ant構建android專案

方案一:基於gradle構建android專案,並實現分包

環境要求:androidstudio0.9以上,gradle外掛程式0.14.2以上

1、如果你的工程在eclipse中,那麼你需要將該工程匯入到android中,此時需要你公升級adt22以上

2、開啟你工程的build.gradle檔案,檢查gradle外掛程式是否是0.14.2版本之後,因為0.14.2之後gradle外掛程式才支援分包

3、開啟工程下某乙個moudle的build.gradle檔案,新增對android-support-multidex.jar的依賴

4、去掉第三方jar包中重複的類

5、設定虛擬機器堆記憶體空間大小,避免在編譯期間oom

6、gradle構建專案時,貌似預設是不會將so庫加入工程的,所以為了避免此種情況發生,我們需要制定so庫目錄,對於從eclipse轉換過來的工程,還需要制定src和資源檔案路徑

7、如果你的專案依賴了其他庫, 分別在各個庫工程中加入 multidexenabled = true 和 jnilibs.srcdirs =['libs']兩個配置即可

attachbasecontext

@override

protected void attachbasecontext(context base)

經過上述配置,你的專案應該是已經成功分包了。如果分包成功,那麼你解壓你的apk檔案,會發現有兩個dex檔案,通過上述的配置過程,我們發現此方案我們無法控制哪些類在main.dex中,哪些類在second.dex中,通過此種方案配置分包,可以相容api4-api20.其載入second.dex採用的是上述方案中的3

下面我們來看看基於ant構建android專案,並實現分包過程

在上述方案中,由於我們無法看到gradle構建專案的指令碼,所以我們無法控制哪些類在第乙個dex,哪些類在第二個dex,此方案中,我們採用ant構建,ant是允許使用者自己定義構建方案的,比如我們可以通過自定義構建方案,將專案中某些第三方jar包放入到second.dex中,關於這個如何實現,請參考開源專案吧

由於該專案載入second.dex所採用的方案是上述方案2,比如second.dex中的某些第三方jar包依賴main.dex中的某些類,這種方案就會實現,所以在此我將此方案去掉,換成了方案3,也就是將second.dex的路徑設定到pathclassloader的載入路徑中

我只給出android 4.4中的解決方案,其他系統大同小異

載入second.dex方法

/**

@param loader

pathclassloader

@additionalclasspathentries

要被載入的dex檔案,這裡就是我們的second.dex

@optimizeddirectory

就是dex檔案解壓的目錄

*/ private static void install(classloader loader, listadditionalclasspathentries,

file optimizeddirectory)

throws illegalargumentexception, illegalacces***ception,

nosuchfieldexception, invocationtargetexception, nosuchmethodexception

field suppressedexceptionsfield =

findfield(loader, "dexelementssuppressedexceptions");

ioexception dexelementssuppressedexceptions =

(ioexception) suppressedexceptionsfield.get(loader);

if (dexelementssuppressedexceptions == null) else

suppressedexceptionsfield.set(loader, dexelementssuppressedexceptions);}}

分包成功後,解壓apk檔案,進入assert資料夾,我們看到如下結構,libs.apk就是第三方jar編譯後形成的dex檔案

Android gradle指令碼學習

參考借鑑 廢話不多說,直接上硬菜 首先新建環境變數gradle home,需要配置為gradle的安裝目錄,然後在path環境變數中配置 gradle home bin gradle home users gradle gradle 4.4 export gradle home export pat...

Android gradle配置抽取合併

當專案中model或library變多過後,比如用到元件化或者引入第三方庫需要配置多個build gradle檔案,一旦需要統一其sdk或者其他元件版本就需要同時修改多個檔案,這確實很麻煩,所以抽取gradle配置非常有必要。抽取過後如果想修改版本,只需修改公共檔案的就可以了。1.新建gradle資...

Android Gradle打包簽名的方式

這裡可以根據自己的需要刪除或者保留 signingconfig signingconfigs.release介紹兩種在gradle中進行打包簽名的方式 signingconfigs 後面的內容請替換為實際值 signingconfigs buildtypes debug 然後在專案的gradle.p...