--main-dex-list 中太多 类,主 dex 容量超出
Too many classes in --main-dex-list, main dex capacity exceeded
我正在尝试 运行 仪器测试用例,但在 dex 转换时出现以下错误
意外的顶级异常:
com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:494)
at com.android.dx.command.dexer.Main.runMultiDex(Main.java:334)
at com.android.dx.command.dexer.Main.run(Main.java:244)
at com.android.dx.command.dexer.Main.main(Main.java:215)
at com.android.dx.command.Main.main(Main.java:106)
:App:dexDebug FAILED
如何解决 gradle 中的这个问题?
你有两个选择。
- 使用 ProGuard 减少方法数量
- 使用 multidex 功能
我的建议 - 使用 ProGuard,它只需对源代码进行零改动
我们先来了解一下问题:
在 Lollipop 之前的设备上,框架仅加载主 dex。要支持多 dex 应用程序,您必须使用所有辅助 dex 文件显式修补应用程序 class 加载器(这就是为什么您的应用程序 class 必须扩展 MultiDexApplication class or call MultiDex#install)。
这意味着您的应用程序的主 dex 应该包含所有 class 在 class 加载程序修补之前可能访问的 es。
如果您的应用程序代码在成功修补应用程序 class 加载程序之前尝试引用打包在您的一个辅助 dex 文件中的 class,您将收到 java.lang.ClassNotFoundException。
我已经 documented here 插件如何决定哪些 class 应该打包到 main-dex 中。
如果这些 classes 引用的方法总数超过 65,536 个限制,则构建将失败并出现 Too many classes in --main-dex-list, main dex capacity exceeded
错误。
对于这个问题,我可以想到三种可能的解决方案:
- (最简单的解决方案,但不适合大多数人
应用程序) 将您的 minSdkVersion 更改为 21。
- 压缩您的应用程序代码。这在之前已经讨论过很多次(参见 here and here)。
- 如果上述 none 解决方案适合您,您可以尝试使用 my workaround 解决此问题 - 我正在修补 Android gradle 插件以不在主索引中包含 Activity classes。这有点hacky,但对我来说效果很好。
Android 错误跟踪器中有 an issue 关于此错误。希望工具团队尽快提供更好的解决方案。
更新 (4/27/2016)
2.1.0 版 Gradle 插件允许过滤 main-dex 列表 classes.
警告:这正在使用不受支持的 api,将来会被替换。
例如,要排除所有 activity classes 你可以这样做:
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
}
}
}
}
}
您还可以查看我的 example project 演示此问题(并应用上述过滤)。
更新 2 (7/1/2016)
Gradle 插件的 2.2.0-alpha4 版本(带有 build-tools v24)最终通过 reducing multidex keep list to a minimum.
解决了这个问题
不应再使用 2.1.0 中不受支持(且未记录)的过滤器。我已经更新了我的 sample project,证明构建现在成功了,没有任何自定义构建逻辑。
解决此问题的另一种方法是从主 DEX 文件中删除带有运行时注释的 类:
android {
dexOptions {
keepRuntimeAnnotatedClasses false
}
}
这对于使用依赖注入框架的应用程序特别有用,因为即使是 Dagger 注释通常也保留在运行时。
我正在尝试 运行 仪器测试用例,但在 dex 转换时出现以下错误 意外的顶级异常:
com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:494)
at com.android.dx.command.dexer.Main.runMultiDex(Main.java:334)
at com.android.dx.command.dexer.Main.run(Main.java:244)
at com.android.dx.command.dexer.Main.main(Main.java:215)
at com.android.dx.command.Main.main(Main.java:106)
:App:dexDebug FAILED
如何解决 gradle 中的这个问题?
你有两个选择。
- 使用 ProGuard 减少方法数量
- 使用 multidex 功能
我的建议 - 使用 ProGuard,它只需对源代码进行零改动
我们先来了解一下问题:
在 Lollipop 之前的设备上,框架仅加载主 dex。要支持多 dex 应用程序,您必须使用所有辅助 dex 文件显式修补应用程序 class 加载器(这就是为什么您的应用程序 class 必须扩展 MultiDexApplication class or call MultiDex#install)。
这意味着您的应用程序的主 dex 应该包含所有 class 在 class 加载程序修补之前可能访问的 es。
如果您的应用程序代码在成功修补应用程序 class 加载程序之前尝试引用打包在您的一个辅助 dex 文件中的 class,您将收到 java.lang.ClassNotFoundException。
我已经 documented here 插件如何决定哪些 class 应该打包到 main-dex 中。
如果这些 classes 引用的方法总数超过 65,536 个限制,则构建将失败并出现 Too many classes in --main-dex-list, main dex capacity exceeded
错误。
对于这个问题,我可以想到三种可能的解决方案:
- (最简单的解决方案,但不适合大多数人 应用程序) 将您的 minSdkVersion 更改为 21。
- 压缩您的应用程序代码。这在之前已经讨论过很多次(参见 here and here)。
- 如果上述 none 解决方案适合您,您可以尝试使用 my workaround 解决此问题 - 我正在修补 Android gradle 插件以不在主索引中包含 Activity classes。这有点hacky,但对我来说效果很好。
Android 错误跟踪器中有 an issue 关于此错误。希望工具团队尽快提供更好的解决方案。
更新 (4/27/2016)
2.1.0 版 Gradle 插件允许过滤 main-dex 列表 classes.
警告:这正在使用不受支持的 api,将来会被替换。
例如,要排除所有 activity classes 你可以这样做:
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
}
}
}
}
}
您还可以查看我的 example project 演示此问题(并应用上述过滤)。
更新 2 (7/1/2016)
Gradle 插件的 2.2.0-alpha4 版本(带有 build-tools v24)最终通过 reducing multidex keep list to a minimum.
解决了这个问题
不应再使用 2.1.0 中不受支持(且未记录)的过滤器。我已经更新了我的 sample project,证明构建现在成功了,没有任何自定义构建逻辑。
解决此问题的另一种方法是从主 DEX 文件中删除带有运行时注释的 类:
android {
dexOptions {
keepRuntimeAnnotatedClasses false
}
}
这对于使用依赖注入框架的应用程序特别有用,因为即使是 Dagger 注释通常也保留在运行时。