參考自:https://developer.android.google.cn/studio/build/multidex.html
我們在開發(fā)Androi應用的時候,有時會出現(xiàn)以下這種錯
//早期版本的構(gòu)建程序會出現(xiàn)這個錯
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
或
//較新版本的 Android 構(gòu)建系統(tǒng)雖然顯示的錯誤不同十绑,但指示的是同一問題
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
這些錯誤都會顯示一個數(shù)字:65536。這個數(shù)子很重要,他代表的是單個Dalvik Executable(DEX)字節(jié)碼文件內(nèi)的代碼可調(diào)用的引用總數(shù)泻云。
關(guān)于64K引用限制
Android 應用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可執(zhí)行字節(jié)碼文件,其中包含用來運行您的應用的已編譯代碼狐蜕。Dalvik Executable 規(guī)范將可在單個 DEX 文件內(nèi)可引用的方法總數(shù)限制在 65,536宠纯,其中包括 Android 框架方法、庫方法以及您自己代碼中的方法层释。在計算機科學領(lǐng)域內(nèi)婆瓜,術(shù)語千(簡稱 K)表示 1024(或 2^10)。由于 65,536 等于 64 X 1024,因此這一限制也稱為“64K 引用限制”廉白。
Android 5.0 之前版本的 Dalvik 可執(zhí)行文件分包支持
Android 5.0(API 級別 21)之前的平臺版本使用 Dalvik 運行時來執(zhí)行應用代碼个初。默認情況下,Dalvik 限制應用的每個 APK 只能使用單個 classes.dex
字節(jié)碼文件猴蹂。要想繞過這一限制院溺,您可以使用 Dalvik 可執(zhí)行文件分包支持庫,它會成為您的應用主要 DEX 文件的一部分磅轻,然后管理對其他 DEX 文件及其所包含代碼的訪問珍逸。
Android 5.0 及更高版本的 Dalvik 可執(zhí)行文件分包支持
Android 5.0(API 級別 21)及更高版本使用名為 ART 的運行時,后者原生支持從 APK 文件加載多個 DEX 文件聋溜。ART 在應用安裝時執(zhí)行預編譯谆膳,掃描 classesN.dex 文件,并將它們編譯成單個 .oat 文件撮躁,供 Android 設(shè)備執(zhí)行摹量。因此,如果您的 minSdkVersion 為 21 或更高值馒胆,則不需要 Dalvik 可執(zhí)行文件分包支持庫。
ART和Dalvik介紹
規(guī)避64K限制
在將您的應用配置為支持使用 64K 或更多方法引用之前凝果,您應該采取措施減少應用代碼調(diào)用的引用總數(shù)祝迂,包括由您的應用代碼或包含的庫定義的方法。下列策略可幫助您避免達到 DEX 引用限制:
- 檢查您的應用的直接和傳遞依賴項 - 確保您在應用中使用任何龐大依賴庫所帶來的好處大于為應用添加大量代碼所帶來的弊端器净。一種常見的反面模式是型雳,僅僅為了使用幾個實用方法就在應用中加入非常龐大的庫。減少您的應用代碼依賴項往往能夠幫助您規(guī)避 dex 引用限制山害。
- 通過 ProGuard 移除未使用的代碼 - 為您的版本構(gòu)建啟用代碼壓縮以運行 ProGuard纠俭。啟用壓縮可確保您交付的 APK 不含有未使用的代碼。
使用這些技巧使您不必在應用中啟用 Dalvik 可執(zhí)行文件分包浪慌,同時還會減小 APK 的總體大小冤荆。
配置您的應用進行 Dalvik 可執(zhí)行文件分包
如果你的 MinSdkVersion
大于等于 21
,只需要在模塊級build.gradle
文件中將multiDexEnabled
設(shè)置為true,
如下:
android {
defaultConfig {
...
minSdkVersion 21
targetSdkVersion 28
multiDexEnabled true
}
...
}
如果MinSdkVersion
小于 21
权纤,就比較麻煩了钓简。
須按如下方式使用 Dalvik 可執(zhí)行文件分包支持庫:
- 修改模塊級
build.gradle
文件以啟用 Dalvik 可執(zhí)行文件分包,并將 Dalvik 可執(zhí)行文件分包庫添加為依賴項汹想,如此處所示:
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.3'
}
-
根據(jù)是否要替換
Application
類外邓,執(zhí)行以下操作之一:-
如果您沒有替換
Application
類,請編輯清單文件古掏,按如下方式設(shè)置<application>
標記中的android:name
:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="android.support.multidex.MultiDexApplication" > ... </application> </manifest>
-
如果您替換了
Application
類损话,請按如下方式對其進行更改以擴展 MultiDexApplicationpublic class MyApplication extends MultiDexApplication { ... }
-
或者,如果您替換了
Application
類,但無法更改基本類丧枪,則可以改為替換attachBaseContext()
方法并調(diào)用MultiDex.install(this)
來啟用 Dalvik 可執(zhí)行文件分包:public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
-
構(gòu)建應用后光涂,Android 構(gòu)建工具會根據(jù)需要構(gòu)建主 DEX 文件 (classes.dex) 和輔助 DEX 文件(classes2.dex 和 classes3.dex 等)。然后豪诲,構(gòu)建系統(tǒng)會將所有 DEX 文件打包到您的 APK 中顶捷。
運行時,Dalvik 可執(zhí)行文件分包 API 使用特殊的類加載器來搜索適用于您的方法的所有 DEX 文件(而不是僅在主 classes.dex 文件中搜索)屎篱。
Dalvik 可執(zhí)行文件分包支持庫的局限性
Dalvik 可執(zhí)行文件分包支持庫具有一些已知的局限性服赎,將其納入您的應用構(gòu)建配置之中時,您應該注意這些局限性并進行針對性的測試:
- 啟動期間在設(shè)備數(shù)據(jù)分區(qū)中安裝 DEX 文件的過程相當復雜交播,如果輔助 DEX 文件較大重虑,可能會導致應用無響應 (ANR) 錯誤。在此情況下秦士,您應該通過 ProGuard 應用代碼壓縮以盡量減小 DEX 文件的大小缺厉,并移除未使用的那部分代碼。
- 由于存在 Dalvik linearAlloc 錯誤(問題 22586)隧土,使用 Dalvik 可執(zhí)行文件分包的應用可能無法在運行的平臺版本早于 Android 4.0(API 級別 14)的設(shè)備上啟動提针。如果您的目標 API 級別低于 14,請務(wù)必針對這些版本的平臺進行測試曹傀,因為您的應用可能會在啟動時或加載特定類群時出現(xiàn)問題辐脖。代碼壓縮可以減少甚至有可能消除這些潛在問題。
- 由于存在 Dalvik linearAlloc 限制(問題 78035)皆愉,因此嗜价,如果使用 Dalvik 可執(zhí)行文件分包配置的應用發(fā)出非常龐大的內(nèi)存分配請求,則可能會在運行期間發(fā)生崩潰幕庐。盡管 Android 4.0(API 級別 14)提高了分配限制久锥,但在 Android 5.0(API 級別 21)之前的 Android 版本上,應用仍有可能遭遇這一限制异剥。
聲明主 DEX 文件中需要的類
為 Dalvik 可執(zhí)行文件分包構(gòu)建每個 DEX 文件時瑟由,構(gòu)建工具會執(zhí)行復雜的決策制定來確定主要 DEX 文件中需要的類,以便應用能夠成功啟動冤寿。如果啟動期間需要的任何類未在主 DEX 文件中提供错妖,那么您的應用將崩潰并出現(xiàn)錯誤 java.lang.NoClassDefFoundError
。
該情況不應出現(xiàn)在直接從應用代碼訪問的代碼上疚沐,因為構(gòu)建工具能識別這些代碼路徑暂氯,但可能在代碼路徑可見性較低(如使用的庫具有復雜的依賴項)時出現(xiàn)。例如亮蛔,如果代碼使用自檢機制或從原生代碼調(diào)用 Java 方法痴施,那么這些類可能不會被識別為主 DEX 文件中的必需項。
因此,如果您收到 java.lang.NoClassDefFoundError
辣吃,則必須使用構(gòu)建類型中的 multiDexKeepFile
或 multiDexKeepProguard
屬性聲明它們动遭,以手動將這些其他類指定為主 DEX 文件中的必需項。如果類在 multiDexKeepFile
或 multiDexKeepProguard
文件中匹配神得,則該類會添加至主 DEX 文件厘惦。
multiDexKeepFile 屬性
您在 multiDexKeepFile 中指定的文件應該每行包含一個類,并且采用 com/example/MyClass.class 的格式哩簿。例如宵蕉,您可以創(chuàng)建一個名為 multidex-config.txt 的文件,如下所示:
com/example/MyClass.class
com/example/MyOtherClass.class
然后节榜,您可以按以下方式針對構(gòu)建類型聲明該文件:
android {
buildTypes {
release {
multiDexKeepFile file('multidex-config.txt')
...
}
}
}
請記住羡玛,Gradle 會讀取相對于 build.gradle 文件的路徑,因此如果 multidex-config.txt 與 build.gradle 文件在同一目錄中宗苍,以上示例將有效稼稿。
multiDexKeepProguard 屬性
multiDexKeepProguard
文件使用與 Proguard 相同的格式,并且支持整個 Proguard 語法讳窟。如需了解有關(guān) Proguard 格式和語法的詳細信息让歼,請參閱 Proguard 手冊中的 Keep Options 一節(jié)。
您在 multiDexKeepProguard
中指定的文件應該在任何有效的 ProGuard 語法中包含 -keep
選項丽啡。例如谋右,-keep com.example.MyClass.class
。您可以創(chuàng)建一個名為 multidex-config.pro
的文件碌上,如下所示:
-keep class com.example.MyClass
-keep class com.example.MyClassToo
如果您想要指定包中的所有類,你可以使用-keep class com.example.** { *; }
然后浦徊,您可以按以下方式針對構(gòu)建類型聲明該文件:
android {
buildTypes {
release {
multiDexKeepProguard('multidex-config.pro')
...
}
}
}