構建具備超過65K個方法的Apps

因爲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

關於65K的引用限制

Android應用程序(APK)文件以Dalvik Executable(DEX)文件的格式包含了可執行的字節碼文件,而DEX包含了用於運行你的app的已編譯代碼。Dalvik Executable規範限制了一個單獨的DEX文件內可被引用的方法的總數爲65,536,包括Android framework方法,庫方法和你本身的代碼中的方法。要繞過這個限制須要你配置你的app的構建過程來產生多個DEX文件,即所謂的multidex配置。 架構

Android 5.0以前multidex支持

Android 5.0以前版本的平臺使用Dalvik runtime來執行app代碼。默認狀況下,Dalvik限制了apps爲每一個APK一個單獨的classes.dex字節碼。爲了繞過這個限制,你可使用multidex support library,它們是你的app的主DEX文件的一部分,來管理對於它們額外包含的代碼和DEX文件的訪問。 app

Android 5.0及更高版本的multidex支持

Android 5.0及更高的版本使用了一個稱爲ART的runtime,它原生支持由應用程序APK文件加載多個dex文件。ART在應用程序安裝時執行預編譯,則安裝時會掃描classes(..N).dex文件並把它們編譯爲一個單獨.oat文件給Android設備執行。關於Android 5.0 runtime的更多信息,請參考Introducing ART ide

避免65K限制

在配置你的app以啓用對 65K或更多方法引用 的使用以前,你應該執行一些步驟來減小你的app代碼調用的引用的總個數,包括你的app代碼定義的方法及庫。下面的作法能夠幫你避免打到dex引用限制: 工具

  • Review你的app的直接和間接依賴 - 確保你的app中包含的對大library的使用,比直接在應用程序中加代碼更有價值。一個常見的很差的作法是,包含了一個很是大的庫,卻僅僅爲了使用其中的幾個utility方法。減小你的app代碼的依賴經常能夠幫你避免dex引用限制。
  • 用ProGuard移除無用的代碼 - 爲你的app配置ProGuard設定來運行ProGuard,並肯定你已經爲release builds打開了shrinking。打開shrinking能夠確保你不會把沒用的代碼放進你的APKs。

使用這些技術能夠幫你避免對構建配置作改動,同時可以在你的app中引用更多的方法。這些步驟也能減少你的APKs的大小,這一點對那些帶寬成本很高的市場特別重要。 post

配置你的App支持Gradle的Multidex

Android SDK Build Tools 21.1及更高版本中的Gradle Android插件支持把multidex做爲你的構建配置的一部分。在試着配置你的app支持multidex以前,請確認你已經使用SDK Manager把Android SDK Build Tools工具和Android Support Repository更新到了最新版。 測試

要設置你的app開發工程使用multidex配置,須要你對你的app開發工程作一些修改。特別地你須要執行下面的這些步驟:

  • 修改你的Gradle構建配置以啓用multidex。

修改你的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構建文件的defaultConfigbuildType,或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的限制

multidex support library有一些已知的限制,你應該對它們有所意識,並在把它合進你的app構建配置時作一些針對行的測試:

  • 在啓動期間將.dex文件安裝到設備的data分區是很複雜的,若是次要的dex文件很是大的話,這可能致使Application Not Responding (ANR) errors。在這種狀況下,你應該應用ProGuard的代碼shrinking技術來最小化dex文件的大小,並移除無用的代碼。
  • 使用了multidex的應用程序,在Android4.0(API level 14)以前版本平臺的設備上,可能因爲一個Dalvik linearAlloc bug (Issue 22586)而沒法啓動。若是你的目標API levels早於14,則請確保針對這些版本的平臺執行了測試,你的應用程序可以啓動,且特定的group of classes可以被加載。代碼shrinking能夠減小或可能消除這些潛在的問題。
  • 使用了multidex配置的應用程序,在運行期間請求大量的內存分配時,可能因爲一個Dalvik linearAlloc限制 (Issue 78035)而crash。Android 4.0 (API level 14)中分配限制增加了,但在Android 5.0 (API level 21) 以前的Android版本上app仍然可能遇到這個限制。
  • 關於Dalvik runtime執行時主dex中須要什麼類 的要求(requirements)很是複雜。Android構建工具更新處理了Android的要求(requirements),但包含的庫可能有一些額外的依賴要求 (requirements),這包括使用introspection,或在native代碼中調用Java方法。在multidex構建工具被更新以容許你指定必須被包含進主dex文件的類以前有些庫可能沒法使用

優化Multidex開發構建

因爲構建系統必須作 關於什麼類必須被放在主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插件作下面的事情:

  1. 把應用程序的每一個模塊 (包括依賴) 構建爲不一樣的dex文件。這一般被稱爲pre-dexing。
  2. 不加修改地將每一個dex文件包含進APK。
  3. 最重要的是,模塊dex文件將不會被結合 (combined),從而耗費大量時間來計算哪些內容要被放進主dex的過程就被省略了。

因爲只須要對修改了的模塊的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() 。

在Android Studio中使用Build Variants

當使用multidex時,對於管理構建過程來講,build variants可能很是有用。Android Studio容許你在UI中選擇這些build variants

Android Studio構建你的app的"devDebug" variant:

  1. 在左邊欄中打開Build Variants窗口。這個選項挨着Favorites
  2. 點擊build variant的名字並選擇一個不一樣的variant,如圖1所示的那樣。

圖1. Android Studio中左邊面板顯示一個build variant的截圖

注意:打開這個窗口的選項只有在你已經使用命令Tools > Android > Sync Project with Gradle Files成功地同步了Android Studio和你的Gradle文件以後纔可用。

測試Multidex Apps

當使用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。

原文

相關文章
相關標籤/搜索