Android分包方案multidex
Android分包方案multidex
1.對於功能越來越複雜的app的兩大問題
-
一:我們自己應用的方法數超過了65536
我們所說的方法數限制,這個方法數包括了jar包,框架,還有我們自己應用的程式碼,當我們應用的程式碼超過65536時,結果如下:我們看到,顯示我們方法的引用是65579.而引用數最大是65536,建議我們開啟分包方案。
二:我們應用的方法數沒有超過65536,但是加上依賴的jar包,框架等,超過了65536(根據方案一的結果,我們應用方法數是65579,那我們刪掉200個方法,就小於65536)
報錯如下:
-
三:方法數並沒有超過65536,編譯也完成了,但是在android2.3以前的系統安裝的時候,會異常中止安裝。
這個問題會發生在Android 2.2以及Android 2.3的裝置上,涉及到一個名為dexopt的程式,全稱dex optimization,即dex檔案優化程式。在優化過程中,dexopt採用一個固定大小的緩衝區(LinearAlloc)來儲存應用中所有方法的資訊,那麼之所以會出現在老版本停止安裝,是因為老版本的緩衝區的大小是5M,而在新版本中,這個緩衝區的大小是8M或者16M,在老版本中更容易超過這個限制。
dexopt的執行過程是在第一次載入dex檔案的時候執行的。這個過程產生了一個ODEX檔案,全稱Optimised Dex。這個ODEX檔案是在安裝過程中從apk裡提取出的可執行檔案,是優化dex產生的,再把apk包裡的dex檔案刪除,這樣就做到了預先提取。如果沒有ODEX檔案,那麼系統會從apk包中提取dex然後再執行。所以優化後可以加快軟體的啟動速度,預先提取,減少對RAM的佔用。
在早期的Android系統中,dexopt會把每一個類的方法id檢索起來,存在一個連結串列結構裡,而這個連結串列的長度是用一個short型別來儲存的,導致方法id的數目不能夠超過65536個。雖然新版本的android系統中,dexopt修復了這個問題,但是老版本的android系統的使用者市場佔有率還是佔一定比例,還是不能放棄這部分使用者的,所以我們在開發中需要對老版本的這個問題進行相容。
2.方法數越界的解決方案
-
外掛化技術
我們可以採用動態載入部分dex,通過將一個dex拆分成兩個或多個dex,解決方法數越界的問題。
外掛化是一套重量級的技術方案,我們需要通過反射來呼叫外掛的類或方法,要使用一套外掛框架來配合,而且外掛化適合一些獨立的模組,相容性問題往往較多,如果只是用於解決方法數越界的話,並不是最好的方案。
-
multidex解決方案
為了解決方法數越界的問題,Google在2014年提出了multidex的解決方案,這個方案主要是針對AndroidStudio和Gradle編譯環境的,將一個dex檔案拆成兩個或多個dex檔案。
不過需要注意的是multidex有一個版本問題,在Android 5.0以前使用multidex需要手動引入Google提供的android-support-multidex.jar這個jar包。這個jar包我們可以在Android SDK目錄下的extras/android/support/multidex/library/libs下找到。而從Android 5.0開始,Andorid預設支援了multidex。
所以,我們就需要注意我們的SDK版本了,如果已經支援了multidex,而我們又把android-support-multidex.jar放在了專案的libs檔案下,就會報錯。
3.在Gradle和程式碼中配置使用Multidex
-
在Gradle中配置使用Multidex
由於Android的Gradle外掛在Android Build Tool 21.1開始支援使用multidex,所以我們需要使用Android Build Tools 21.1及以上版本,修改app目錄下的build.gradle檔案,有兩點需要修改。
(1)在defaultConfig中新增multiDexEnabled true這個配置項。
(2)在dependencies中新增multidex的依賴:
compile ‘com.android.support:multidex:1.0.0’注意buildToolsVersion要高於21.1,配置好如下:
-
在Gradle中配置好之後,我們還需要在程式碼中加入支援multidex的功能,有三種方案可選
方案一:在manifest檔案中指定Application為MultiDexApplication,如下:
方案二:寫一個Application類並繼承MultiDexApplication,並在AndroidManifest.xml的application標籤中進行註冊(在application標籤中增加name屬性,並新增自己的Application類名即可),如果不是想重寫MultiDexApplication中一些方法的話,還是方案一更方便些。如下:
註冊如下:
方案三:如果不想按方案二繼承,我們可以重寫Application的attachBaseContext方法,注意,這個方法比onCreate方法先執行。具體方法是建立一個新類,繼承Application,然後重寫attachBaseContext方法,並在AndroidManifest.xml的application標籤中進行註冊(與方案二註冊相同)如下:
對於在AndroidManifest.xml中註冊,與方案二的註冊相同。
3.使用Multide分包後兩種情況的結果
我們的Demo圖如下,我們根據該圖和dex檔案反編譯的結果分析分包情況。
-
情況一:方法數沒有越界
我們將方法數控制在65536以內,方法數沒有越界的話,是不會分包的,解壓apk,你會發現apk裡只有一個classes.dex,如下將其反編譯後(不知道怎麼反編譯的可以看一下這篇博文:http://blog.csdn.net/gaozhan_csdn/article/details/51984056),結果如下:
可以發現,我們的類都在這個主dex檔案裡,並沒有分包。
-
情況二:方法數越界
我們再將方法數增加到65536以上。解壓apk,結果如下:
對三個dex檔案反編譯一下,看看它們裡面分別都包含了什麼類。
classes.dex(主dex)下的類檢視:classes2.dex的類檢視
classes3.dex的類檢視
可以發現Second類和Third類分別在classes2.dex檔案和classes3.dex檔案裡,其他類都在主dex檔案裡(classes.dex),我們用multidex的確實現了分包從而解決了方法數越界的問題。
4.使用MultiDex存在的一些問題
1.Application 中的靜態全域性變數會比MutiDex的 instal()方法優先載入,所以建議避免在Application類中使用靜態變數引用main classes.dex檔案以外dex檔案中的類。
或者這樣解決:
一些在二級Dex載入之前,可能會被呼叫到的類(比如靜態變數的類),需要放在主Dex中.否則會ClassNotFoundError. 通過修改Gradle,可以顯式的把一些類放在Main Dex中.
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString()
}
}
注意上面是修改後的Gradle,其中是一個文字檔案的檔名,存放在和這個build.gradle指令碼同一級的檔案目錄下,而不是 專案根目錄。可以把這個文字檔案起名為multidex.keep,內容如下.實際就是把需要放在Main Dex的類羅列出來.
android/support/multidex/BuildConfig/class
android/support/multidex/MultiDex$V14/class
android/support/multidex/MultiDex$V19/class
android/support/multidex/MultiDex$V4/class
android/support/multidex/MultiDex/class
android/support/multidex/MultiDexApplication/class
android/support/multidex/MultiDexExtractor$1/class
android/support/multidex/MultiDexExtractor/class
android/support/multidex/ZipUtil$CentralDirectory/class
android/support/multidex/ZipUtil/class
project.afterEvaluate標籤在特定的project配置完成後執行,而gradle.projectsEvaluated在所有projects配置完成後執行。 注意afterEvaluate需要放在android{}裡,不可放外面。
但是最新的as中,會自動判斷依賴關係來分dex,比如以下application中:
public class MyApp extends MultiDexApplication {
public static MutilTest5 mutilTest5 = new MutilTest5();
@Override
public void onCreate() {
super.onCreate();
}
}
預設情況下,本來MuitlText5要分到class2.dex裡面去,但是因為app裡靜態變數需要用到MuitlText5,如果放到class2.dex中會找不到(因為app中靜態變數初始化會在載入主dex檔案之前執行),所以會自動放到主dex檔案裡去
但是如果依靠as自動分析,在你程式碼存在反射和native的情況下,也不保證100%正確,如果不正確,還是需要自己配置哪個類放到主dex中。
轉自:https://www.cnblogs.com/chenxibobo/p/6076459.html
擴充套件:其實你不知道MultiDex到底有多坑:https://www.cnblogs.com/tonny-li/p/7839306.html
Android-Multidex安裝流程解析: https://blog.csdn.net/dbs1215/article/details/79214565