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
现在不被支持。