为 Dalvik 可执行文件分包构建每个 DEX 文件时,构建工具会执行复杂的决策制定来确定主要 DEX 文件中需要的类,以便应用能够成功启动。如果启动期间需要的任何类未在主 DEX 文件中提供,那么您的应用将崩溃并出现错误java.lang.NoClassDefFoundError。
该情况不应出现在直接从应用代码访问的代码上,因为构建工具能识别这些代码路径,但可能在代码路径可见性较低(如使用的库具有复杂的依赖项)时出现。例如,如果代码使用自检机制或从原生代码调用 Java 方法,那么这些类可能不会被识别为主 DEX 文件中的必需项。
因此,如果您收到java.lang.NoClassDefFoundError,则必须使用构建类型中的multiDexKeepFile或multiDexKeepProguard属性声明它们,以手动将这些其他类指定为主 DEX 文件中的必需项。如果类在multiDexKeepFile或multiDexKeepProguard文件中匹配,则该类会添加至主 DEX 文件。
multiDexKeepFile 属性
您在multiDexKeepFile中指定的文件应该每行包含一个类,并且采用com/example/MyClass.class的格式。例如,您可以创建一个名为multidex-config.txt的文件,如下所示:
com/example/MyClass.class
com/example/MyOtherClass.class
然后,您可以按以下方式针对构建类型声明该文件:
android{
buildTypes{
release{
multiDexKeepFile file'multidex-config.txt'
...
}
}
}
请记住,Gradle 会读取相对于build.gradle文件的路径,因此如果multidex-config.txt与build.gradle文件在同一目录中,以上示例将有效。
multiDexKeepProguard 属性
multiDexKeepProguard文件使用与 Proguard 相同的格式,并且支持整个 Proguard 语法。如需了解有关 Proguard 格式和语法的详细信息,请参阅 Proguard 手册中的Keep Options一节。
您在multiDexKeepProguard中指定的文件应该在任何有效的 ProGuard 语法中包含-keep选项。例如,-keep com.example.MyClass.class。您可以创建一个名为multidex-config.pro的文件,如下所示:
-keepclasscom.example.MyClass
-keepclasscom.example.MyClassToo
如果您想要指定包中的所有类,文件将如下所示:
-keepclasscom.example.**{*;}// All classes in the com.example package
然后,您可以按以下方式针对构建类型声明该文件:
android{
buildTypes{
release{
multiDexKeepProguard'multidex-config.pro'
...
}
}
}
优化开发构建中的 Dalvik 可执行文件分包
Dalvik 可执行文件分包配置会大幅增加构建处理时间,因为构建系统必须就哪些类必须包括在主 DEX 文件中以及哪些类可以包括在辅助 DEX 文件中作出复杂的决策。这意味着使用 Dalvik 可执行文件分包的增量式构建通常耗时更长,可能会拖慢您的开发进度。
为了缩短耗时更长的 Dalvik 可执行文件分包输出构建时间,请利用productFlavors(一个开发定制和一个发布定制,具有不同的minSdkVersion值)创建两个构建变型。
对于开发定制,将minSdkVersion设置为 21。该设置将启用一个名为pre-dexing的构建功能,此功能使用仅适用于 Android 5.0(API 级别 21)和更高版本的 ART 格式更快生成 Dalvik 可执行文件分包输出。对于发布定制,将minSdkVersion设置为适于您的实际最低支持级别。此设置生成的 Dalvik 可执行文件分包 APK 可兼容更多设备,但构建时间更长。
以下构建配置示例展示了如何在 Gradle 构建文件中设置这些定制:
android{
defaultConfig{
...
multiDexEnabledtrue
}
productFlavors{
dev{
// Enable pre-dexing to produce an APK that can be tested on
// Android 5.0+ without the time-consuming DEX build processes.
minSdkVersion21
}
prod{
// The actual minSdkVersion for the production version.
minSdkVersion14
}
}
buildTypes{
release{
minifyEnabledtrue
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies{
compile'com.android.support:multidex:1.0.1'
}
您完成此配置变更后,可以为增量式构建使用应用的devDebug变体,后者集dev产品定制与debug构建类型的属性于一身。这将创建已启用 Dalvik 可执行文件分包且禁用 proguard 的可调试应用(因为minifyEnabled默认为false)。这些设置会使适用于 Gradle 的 Android 插件执行以下操作:
执行 pre-dexing:将每个应用模块和每个依赖项构建为单独的 DEX 文件。
将每个 DEX 文件加入 APK,并且不做任何修改(不执行代码压缩)。
最重要的是,模块 DEX 文件不执行合并操作,因此可以避免为确定主 DEX 文件的内容而进行长时间的计算。
这些设置的好处是,可以进行快速的增量式构建,因为只有修改过的模块的 DEX 文件才会在后续构建期间重新计算并重新打包。但是,这些构建的 APK 只能用于在 Android 5.0 设备上进行测试。不过,由于是以定制形式实现配置,您保留了使用与发布相适的最低 API 级别和 ProGuard 代码压缩执行正常构建的能力。
您还可以构建其他变体,包括prodDebug变体构建,该变体虽然构建时间更长,但可用于开发以外的测试。在所示配置内,prodRelease变体将是最终测试和发布版本。如需了解有关使用构建变体的详细信息,请参阅配置构建变体。
提示:由于您有适用于不同 Dalvik 可执行文件分包需求的不同构建变体,因此也可以为不同变体提供不同清单文件(这样,只有适用于 API 级别 20 和更低版本的清单文件会更改标记名称),或者为每个变体创建不同的Application子类(这样,只有适用于 API 级别 20 和更低版本的清单文件会扩展MultiDexApplication类或调用MultiDex.install(this))。
测试 Dalvik 可执行文件分包应用
编写面向 Dalvik 可执行文件分包应用的仪器测试时,无需进行其他配置。AndroidJUnitRunner直接支持 Dalvik 可执行文件分包,前提是您使用MultiDexApplication或替换您的自定义Application对象中的attachBaseContext() 方法,并调用MultiDex.install(this)以启用 Dalvik 可执行文件分包。
或者,您可以替换 AndroidJUnitRunner 中的onCreate()方法:
publicvoidonCreate(Bundlearguments){
MultiDex.install(getTargetContext());
super.onCreate(arguments);
...
}
注:目前不支持使用 Dalvik 可执行文件分包来创建测试 APK。