標籤:並且 運行 short enabled err 類庫 動態 keyword 匯入工程
如有轉載,請聲明出處: 時之沙: http://blog.csdn.net/t12x3456 (來自時之沙的csdn部落格)
隨著應用不斷迭代,業務線的擴充,應用越來越大(比如整合了各種第三方sdk或者公用支援的jar包,項目耦合性高,重複作用的類越來越多),相信很多人都遇到過如下的錯誤:
[java] view plain copy
- UNEXPECTED TOP-LEVEL EXCEPTION:
- java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
- at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)
- at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)
- at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)
- at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)
- at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
- at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
- at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
- at com.android.dx.command.dexer.Main.run(Main.java:230)
- at com.android.dx.command.dexer.Main.main(Main.java:199)
- at com.android.dx.command.Main.main(Main.java:103)
沒錯,你的應用中的Dex 檔案方法數超過了最大值65536的上限,簡單來說,應用爆棚了.
那麼讓我們看一下為什麼會引起這種錯誤:
在Android系統中,一個App的所有代碼都在一個Dex檔案裡面。Dex是一個類似Jar的儲存了多有Java編譯位元組碼的歸檔檔案。因為Android系統使用Dalvik虛擬機器,所以需要把使用Java Compiler編譯之後的class檔案轉換成Dalvik能夠執行的class檔案。這裡需要強調的是,Dex和Jar一樣是一個歸檔檔案,裡面仍然是Java代碼對應的位元組碼檔案。當Android系統啟動一個應用的時候,有一步是對Dex進行最佳化,這個過程有一個專門的工具來處理,叫DexOpt。DexOpt的執行過程是在第一次載入Dex檔案的時候執行的。這個過程會產生一個ODEX檔案,即Optimised Dex。執行ODex的效率會比直接執行Dex檔案的效率要高很多。 但是在早期的Android系統中,DexOpt的LinearAlloc存在著限制: Android 2.2和2.3的緩衝區只有5MB,Android 4.x提高到了8MB或16MB。當方法數量過多導致超出緩衝區大小時,會造成dexopt崩潰,導致無法安裝.
另外由於DEX檔案格式限制,一個DEX檔案中method個數採用使用原生類型short來索引檔案中的方法,也就是最多表達65536個method,field/class的個數也均有此限制。對於DEX檔案,則是將工程所需全部class檔案合并且壓縮到一個DEX檔案期間,也就是Android打包的DEX過程中, 單個DEX檔案可被引用的方法總數被限制為65536(自己開發以及所引用的Android Framework和第三方類庫的代碼).
目前比較常用的方法:(1) 應用外掛程式化,比如使用我正在參與開發的外掛程式化架構 : https://github.com/singwhatiwanna/dynamic-load-apk ,如果有建議或者相關的問題,歡迎到Github上積极參与. (2) 分割Dex,多工程: 把所需要的.class檔案或者是Jar檔案和一些源碼一起編譯產生一個Jar檔案。然後使用Android SDK提供的dx工具把Jar檔案轉成Dex檔案。(可參考facebook:https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920,這裡邊還可以看到在2.3上動態改變LinearAlloc緩衝的解決思路) 這兩種方法並不衝突,外掛程式化除瞭解決應用爆棚,還有很多其他的優點,可以看我之前的文章,不再複述.
當然,Google看來也意識到了目前應用方法數爆棚的問題, 目前在已經在API 21中提供了通用的解決方案,那就是android-support-multidex.jar. 這個jar包最低可以支援到API 4的版本(Android L及以上版本會預設支援mutidex).
讓我們看一下如何應用android-support-multidex.jar(以下都以在Anroid studio中的使用為例,使用eclipse開發需要安裝gradle外掛程式,其他基本上相同):
首先可以用--multi-dex配置(build.gradle)進行解決,產生的Apk中將包含多個dex檔案,比如classes.dex, classes2.dex. 如下,需要對build.gradle進行修改:
[java] view plain copy
- afterEvaluate {
- tasks.matching {
- it.name.startsWith(‘dex‘)
- }.each { dx ->
- if (dx.additionalParameters == null) {
- dx.additionalParameters = []
- }
- dx.additionalParameters += ‘--multi-dex‘ // enable multidex
-
- // optional
- // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list
- }
- }
但是預設的Dalvik 類載入器只會尋找classes.dex,所以需要將它們進行合并才能使得被識別
當然,現在有了android.support.multidex.jar的支援,一切都會變得非常簡單,首先我們看一下相關源碼的目錄,具體的原理分析我會在之後的文章中進行講解:
[java] view plain copy
- 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
具體整合:
將如下配置加入工程 build.gradle中
[java] view plain copy
- android {
- defaultConfig {
- // Enabling multidex support.
- multiDexEnabled true
- }
- }
- dependencies { compile ‘com.google.android:multidex:0.1‘}
MultiDex實現原理:
Apk在啟動並執行時候,有一個dexpathlist,而Multidex的源碼中,會根據你的系統版本號碼對dexpathlist做修改,將所有的dex都添加到dexpathlist中.
接下來整合有兩個步驟:
一. 從sdk\extras\android\support\multidex\library\libs 目錄將android-support-multidex.jar匯入工程中
二. 如果你的工程中已經含有Application類,那麼讓它繼承android.support.multidex.MultiDexApplication類,
如果你的Application已經繼承了其他類並且不想做改動,那麼還有另外一種使用方式,覆寫attachBaseContext()方法:
[java] view plain copy
- public class MyApplication extends FooApplication {
- @Override
- protected void attachBaseContext(Context base) {
- super.attachBaseContext(base);
- MultiDex.install(this);
- }
- }
最後給出build.gradle中的完整配置:
[java] view plain copy
- 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‘
- }
Android 使用android-support-multidex解決Dex超出方法數的限制問題,讓你的應用不再爆棚(轉)