首先,让我们了解这个问题:
在 Lollipop 之前的设备上,框架只会加载主 dex。为支持多 dex 应用程序,您必须显式地使用所有辅助 dex 文件来修补应用程序类加载器(这就是为什么您的 Application 类必须扩展 MultiDexApplication 类或调用 MultiDex#install)。
这意味着您的应用程序的主 dex 应该包含在类加载器修补之前可能访问的所有类。
如果您的应用程序代码尝试引用打包在其中一个辅助 dex 文件中的类,则会收到 java.lang.ClassNotFoundException 错误。
我在此处记录了插件如何决定应该将哪些类打包到主 dex 中。
如果这些类引用的方法总数超过了 65536 限制,则构建将失败,并显示“Too many classes in --main-dex-list, main dex capacity exceeded”错误。
我想到了三种可能的解决方案:
- (最简单的解决方案,但不适用于大多数应用程序)将 minSdkVersion 改为 21。
- 缩小应用程序代码。这在以前已经讨论过(请参见此处和此处)。
- 如果以上解决方案都不适用于您,则可以尝试使用我的解决方法 - 我正在修补 Android Gradle 插件,以便不包括 Activity 类在主 dex 中。它有点 hacky,但对我来说很有效。
Android bug tracker中有关此错误的问题。希望工具团队能够很快提供更好的解决方案。
更新(2016 年 4 月 27 日)
Gradle 插件的版本 2.1.0 允许筛选 main-dex 列表类。
警告:这使用的是将来会被替换的不受支持的 api。
例如,要排除所有 activity 类,可以执行以下操作:
afterEvaluate {
project.tasks.each { task ->
if (task.name.startsWith('collect') && task.name.endsWith('MultiDexComponents')) {
println "main-dex-filter: found task $task.name"
task.filter { name, attrs ->
def componentName = attrs.get('android:name')
if ('activity'.equals(name)) {
println "main-dex-filter: skipping, detected activity [$componentName]"
return false
} else {
println "main-dex-filter: keeping, detected $name [$componentName]"
return true
}
}
}
}
}
你可以查看我的示例项目来演示这个问题(并应用上述过滤方法)。
更新2(7/1/2016)
Gradle插件的2.2.0-alpha4版本(使用build-tools v24)通过将multidex保留列表减少到最小终于解决了这个问题。
不再需要使用不支持的(且未文档化的)2.1.0中的过滤器。我已经更新了我的示例项目,演示构建现在可以在没有任何自定义构建逻辑的情况下成功。