因为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。
原文。