因爲Android平臺的持續成長,Android apps的大小也同樣不斷變大。當你的應用程序及其引用的庫達到某個大小時,你將遇到一個 表示你的app已經達到了Android app構建架構的一個限制 的build errors。早些時候的構建系統將報出一個相似下面這樣的一個error: html
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
更近一些的Android構建系統版本則顯示一個不一樣的error,但指示了相同的問題: java
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
從這兩個errors中能夠看到一個共同的數字:65,536。這個數字很重要,它表示一個單獨的Davlik Executable(dex)字節碼文件中的代碼能夠調用的引用的總個數。若是你在構建一個Android app且遇到了這個error,那麼恭喜你,你的代碼量很是大!這份文檔解釋了要如何繞過這個限制並繼續構建你的app。 android
注意:這份文檔中提供的指南取代了在Android Developers blog post Custom Class Loading in Dalvik中給出的指南。 shell
Android應用程序(APK)文件以Dalvik Executable(DEX)文件的格式包含了可執行的字節碼文件,而DEX包含了用於運行你的app的已編譯代碼。Dalvik Executable規範限制了一個單獨的DEX文件內可被引用的方法的總數爲65,536,包括Android framework方法,庫方法和你本身的代碼中的方法。要繞過這個限制須要你配置你的app的構建過程來產生多個DEX文件,即所謂的multidex配置。 架構
Android 5.0以前版本的平臺使用Dalvik runtime來執行app代碼。默認狀況下,Dalvik限制了apps爲每一個APK一個單獨的classes.dex字節碼。爲了繞過這個限制,你可使用multidex support library,它們是你的app的主DEX文件的一部分,來管理對於它們額外包含的代碼和DEX文件的訪問。 app
Android 5.0及更高的版本使用了一個稱爲ART的runtime,它原生支持由應用程序APK文件加載多個dex文件。ART在應用程序安裝時執行預編譯,則安裝時會掃描classes(..N).dex文件並把它們編譯爲一個單獨.oat文件給Android設備執行。關於Android 5.0 runtime的更多信息,請參考Introducing ART。 ide
在配置你的app以啓用對 65K或更多方法引用 的使用以前,你應該執行一些步驟來減小你的app代碼調用的引用的總個數,包括你的app代碼定義的方法及庫。下面的作法能夠幫你避免打到dex引用限制: 工具
使用這些技術能夠幫你避免對構建配置作改動,同時可以在你的app中引用更多的方法。這些步驟也能減少你的APKs的大小,這一點對那些帶寬成本很高的市場特別重要。 post
Android SDK Build Tools 21.1及更高版本中的Gradle Android插件支持把multidex做爲你的構建配置的一部分。在試着配置你的app支持multidex以前,請確認你已經使用SDK Manager把Android SDK Build Tools工具和Android Support Repository更新到了最新版。 測試
要設置你的app開發工程使用multidex配置,須要你對你的app開發工程作一些修改。特別地你須要執行下面的這些步驟:
修改你的app Gradle構建文件配置包含support library並啓用multidex輸出,以下面的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' }
注意:你能夠在Gradle構建文件的defaultConfig,buildType,或productFlavor段中指定multiDexEnabled設定。
在你的manifest中向你的application元素添加來自於multidex support library的MultiDexApplication類。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.multidex.myapplication"> <application ... android:name="android.support.multidex.MultiDexApplication"> ... </application> </manifest>
給app添加了這些配置設定以後,Android構建工具會構建一個主dex(classes.dex),並在須要時構建一些supporting(classes2.dex,classes3.dex)。構建系統將把它們打包進一個APK以用於發佈。
注意:若是你的app繼承了Application類,你能夠覆寫attachBaseContext()方法並調用MultiDex.install(this)以啓用multidex。要了解更多信息,請查看MultiDexApplication參考文檔。
multidex support library有一些已知的限制,你應該對它們有所意識,並在把它合進你的app構建配置時作一些針對行的測試:
因爲構建系統必須作 關於什麼類必須被放在主DEX文件而什麼類能夠被放入次要DEX文件 的複雜決定,一個multidex配置請求 可能會使構建過程的時間大爲增長。這意味着做爲開發過程一部分而執行的構建例程,在具備multidex時,將消耗更多時間,並可能下降開發過程的速度。
爲了延緩multidex輸出形成的構建時間增加,你應該使用Gradle Android插件的productFlavors,在你的構建輸出上建立兩個variantions:一個開發flavor和一個產品flavor。
對於開發flavor,設置最小SDK版本爲21。這種設定將使用ART支持的格式產生multidex輸出,這要快得多。對於release flavor,設置最小SDK版本爲你實際要支持的最小版本。這個設定產生一個multidex APK,它能與更多的設備兼容,但構建也會耗費更多的時間。
下面的構建配置示例演示瞭如何在一個Gradle構建文件中設置這些flavors:
android { productFlavors { // Define separate dev and prod product flavors. dev { // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin // to pre-dex each module and produce an APK that can be tested on // Android Lollipop without time consuming dex merging processes. minSdkVersion 21 } prod { // The actual minSdkVersion for the application. minSdkVersion 14 } } ... buildTypes { release { runProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile 'com.android.support:multidex:1.0.0' }
在完成了這個配置改動以後,你可使用你的app的devDebug variant,它結合了dev productFlavor和debug buildType的屬性。使用這個target將建立一個debug app,其中禁掉了proguard,啓用了multidex,minSdkVersion被設置爲了Android API level 21。這些設定將使得Android gradle插件作下面的事情:
因爲只須要對修改了的模塊的dex文件進行從新計算並打包進APK,這些設定致使了快速的,增量構建。這些構建的結果APK只能用於Android 5.0設備上的測試。然而,經過將配置實現爲一個flavor,你將保有爲release執行普通的構建的能力 - 適當的最小SDK level及proguard設定。
你也能夠構建其它的variants,包括prodDebug variant build,它須要花費更多的時間來構建,但可被用於開發以外的測試。在展現的配置中,prodRelease variant將是最終的測試和發佈版。若是你在經過命令行執行gradle tasks,你可使用在最後附加了DevDebug的標準命令 (好比./gradlew installDevDebug)。更多關於使用Gradle tasks的flavors的信息,請查看Gradle Plugin User Guide。
提示:你也能夠爲每一個flavor提供一個定製的manifest,或一個定製的application,這使你可使用support library MultiDexApplication類,或只在須要的variant中調用MultiDex.install() 。
當使用multidex時,對於管理構建過程來講,build variants可能很是有用。Android Studio容許你在UI中選擇這些build variants。
讓Android Studio構建你的app的"devDebug" variant:
圖1. Android Studio中左邊面板顯示一個build variant的截圖。
注意:打開這個窗口的選項只有在你已經使用命令Tools > Android > Sync Project with Gradle Files成功地同步了Android Studio和你的Gradle文件以後纔可用。
當使用instrumentation測試multidex apps時,須要一些額外的配置來啓用test instrumentation。因爲在multidex apps中,類的代碼的位置不是在一個單獨的DEX文件中的,則除非針對multidex作了配置,不然instrumentation tests沒法適當地運行。
要用instrumentation tests測試一個multidex app,則配置multidex testing support library的MultiDexTestRunner。下面的示例build.gradle文件演示瞭如何配置你的構建來使用這個test runner:
android { defaultConfig { ... testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner" } }
注意:使用Gradle版本低於1.1的Android Plugin時,你須要爲multidex-instrumentation添加下面的依賴:
dependencies { androidTestCompile('com.android.support:multidex-instrumentation:1.0.1') { exclude group: 'com.android.support', module: 'multidex' } }
你可能直接使用instrumentation test runner類或繼承它來適應你的測試須要。或者你能夠像下面這樣在現有的instrumentations中覆寫onCreate:
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }
注意:如今還不支持使用multidex來建立一個測試APK。
Done。
原文。