Android App的大小随着Android平台持续增长。当你的应用程序和它引用的库达到某个大小,你会遇见预示你的App已经达到Android应用构建架构极限的构建错误。早期版本的构建系统报告如下错误:
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
最近版本的Android构建系统显示一个不同的错误,他指示这同样的问题:
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
这些错误状况都显示一个相同的数字:65536。这个数字有重要意义,它代表了在一个单独的Dalvik Executable字节文件中可以被调用代码的总数。如果你构建一个Android应用并且发生了这个错误,那么恭喜你,你有许多的代码!这个文档讲解了如何摆脱这个限制继续构建你的应用。
注意:本文档提供的指导取代了在Android开发者博客中发布的Custom Class Loading in Dalvik指导。
大约65千引用限制
—————————————————————————————————————————
Android应用文件(APK)包以含Davik Executable形式(DEX)的可执行字节码文件,它包含了用于你应用执行的编译代码。Davik Executable规范限制在一个DEX文件中可以被调用的方法的总数为65536,包含Android framework方法,库方法,和你自己的代码方法。为了打破这个限制要求,你可以配置你的应用构建进程生成多个DEX文件,被称为multidex配置。
Android5.0之前的Mutidex支持
Android5.0之前的平台版本使用Dalvik运行时执行应用代码。默认,Dalvik限制应每个APK只有一个classes.dex字节码文件。为了绕过这个限制,你可以使用multidex support library,它开始启动你的应用的主要DEX文件,然后控制访问额外的DEX文件和它们的代码;
Android5.0和更高的Mutidex支持
Android5.0和更高使用一个称为ART的运行时,它天然的支持应用APK文件多个dex文件。ART在应用安装时执行预编译,它扫描classes(…N).dex文件并且将它们编译成一个.oat文件在Android设备上执行。更多关于Android5.0运行时的信息,请查阅introducing ART。
避免65K限制
—————————————————————————————————————————
在配置你的应用程序启动使用超过65K方法引用之前,你应该采取措施来减少你的应用代码调用引用的总数,
包含你的应用代码或者包含库的方法定义。下面的策略能帮助你避免突破dex引用限制:
- 检查你的应用的直接和传递依赖-确保在你的应用中你包含的任何大的库依赖以某种方式被使用,那么被添加到应用程序中的代码数量偏大。相反面就是由于很少的实体方法是有用的而包含一个非常大的库。减少你的应用代码依赖通常能帮助你避免dex引用限制。
- 使用ProGuard移除没有使用的代码-为你的应用配置ProGuard设置运行ProGuard,并且确保你为release构建启动了压缩。启动压缩确保你没有在你的APK中装载没有使用的代码。
使用这些技术能帮助你在你的应用中避免为了启动更多的方法引用,而要求的配置改变。这些不能也能减少你的APK的大小,它对带宽成本特别高的市场来说特别重要。
在Gradle中配置你的应用支持Multidex
—————————————————————————————————————————
在Android SDK Build Tools 21.1和更高提供的Android Gradle插件,支持multidex作为你的构建配置的一部分。在为你的应用尝试配置multidex之前,确保你更新了Android SDK Build Tools并且Android Support Repository是最新的版本。
设置你的应用程序开发项目使用multidex配置指需要你的应用开发项目做很小的改动。你需要执行下面的步骤:
- 改变你的Gradle构建配置来启动multidex
- 修改你的清单文件引用MultiDexApplication类
就像下面Gradle构建文件代码段中展示的,修改你的应用的Gradle构建文件配置来包含支持库并且启动multidex输出。
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设置。
在你的清单文件中添加MultiDexApplication类,从multidex支持库添加到你的应用程序元素。
<?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>
当这些配置设置被添加到一个应用中,Android构建工具根据需要创建一个主要的dex(classes.dex)和支持(classes2.dex,classes3.dex)。构建系统然后将会将他们打包到一个APK文件中用于发布;
注意:如果你的应用使用继承Application类,你可以覆盖attachBaseContext()方法和调用MultiDex.install(this)来启动multidex。更多的信息,请查阅MultiDexApplication参考文档。
multidex支持库的局限
—————————————————————————————————————————
multidex支持库有一些总所周知的局限,那么当你向你的应用构建配置中引入它的时候应该关注并测试它:
- 在启动到一台设备的数据分组时,.dex文件的安装是复杂的,并且如果第二个dex文件非常大,可能导致应用程序无法响应(ANR)错误。在这种情况下,你应该使用ProGuard代码缩减技术来减少dex文件的大小,并且移除未使用部分的代码。
- 使用multidex的应用程序可能在比Android4.0(API Level 14)更早的
… …
优化Multidex开发构建
—————————————————————————————————————————
multidex配导致构建处理的时间显著的增加,因为构建系统必须做出复杂的决策,关于什么类必须被包含在最初的Dex文件中,和什么类能被包含在第二个Dex文件。这就意味着使用multidex的日常构建,作为开发过程的一部分被执行的更长,并且会减缓你的开发进程。
为了multidex输出减轻更长的构建时间,你应该使用Android Gradle插件productFlavors:在你的构建输出上创建两个变量:一个开发Flavor和一个生产flavor。
对于开发Flavor,设置最小的SDK版本为21。这个设置使用支持ART格式生成multidex输出更快。对于生成Flavor,设置最低的SDK版本和你实际支持的最小级别一样。这个设置生成一个兼容更多设备的multidex APK,但是花费更长时间来构建。
下面的构建配置例子演示了如何在Gradle构建文件中设置这些Flavor:
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‘ }
在你完成所有这些配置改变之后,你能使用你的应用的devDebug实体,它结合了dev生产Flavor属性和debug构建类型。使用这个不启用proguard,支持multidex,并且最先的SDK版本设置为Android API level 21的目标创建一个debug应用。这些设置使得Android gradle插件执行如下工作:
- 构建应用程序的每个模块(包含依赖)作为单独的dex文件。这通常被称为pre-dexing。
- 在API中包含未修改的每个dex文件。
- 最重要的是,module dex文件不会被结合,和决定最初的dex文件内容的大量时间被避免。
这些设置使得快速、增量构建,因为仅仅被修改模块的dex文件被重新计算,并且打包到APK文件中。这些设置产生的APK仅仅能被用在Android5.0设备上测试。然而,通过实现了flavor形式的设置,你保留了在合适的生产最新SDK级别和proguard设置执行正常构建的能力。
你也能构建其它的实体,包含一个proDebug实体构建,他花费更长的时间,但是能被用于测试以外的开发。
… …
在Android Studio中使用构建实体
构建实体对于管理构建进程什么时候使用multidex非常有用。Android允许你在用户界面选择这些构建实体。
为了让Android Studio构建你的应用的"devDebug"实体:
- 在左侧栏中打开Build实体窗口,选项位于Favorites旁边。
- 点击构建实体的名字来选择不同的实体,如图1所示。
注意:打开窗口中的选项仅仅在你使用Tools>Android>Sync Project with Gradle Files命令,使用你的Gradle构建文件成功同步Android Studio之后才有效。
测试Multidex应用
—————————————————————————————————————————
测试使用了multidex配置的应用需要一些额外的步骤和配置。因为类的代码位置没有在一个单独的DEX文件中,instrumentation测试无法正常运行,除非配置支持multidex。
当测使用instrumentation测试一个multidex应用的时候,使用来自multidex测试支持库中的MultiDexTestRunner。下面的build.gradle文件例子,演示了如何配置你的构架使用这个test runner:
android { ? defaultConfig { ? ? ? ... ? ? ? testInstrumentationRunner "android.support.multidex.MultiDexTestRunner" ? } } dependencies { ? ? androidTestCompile ‘com.android.support:multidex-instrumentation:1.0.0‘ }
你可能直接或者继承使用instrumentation测试runner类,来满足你的测试需要。或者你可以像在存在的instrumentations中这样覆盖onCreate:
public void onCreate(Bundle arguments) { ? ? MultiDex.install(getTargetContext()); ? ? super.onCreate(arguments); ? ? ... }
注意:使用multidex创建一个测试APK现在不被支持。