--main-dex-list中的类太多,超出了主要的dex容量

Sre*_*yan 27 dex android-gradle-plugin

我正在尝试运行检测测试用例,但在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
Run Code Online (Sandbox Code Playgroud)

如何在gradle中解决此问题?

Ale*_*pov 23

我们先来了解一下这个问题:

在前Lollipop设备上,框架仅加载主要dex.要支持多dex应用程序,您必须使用所有辅助dex文件显式修补应用程序类加载器(这就是为什么Application类必须扩展MultiDexApplication类或调用MultiDex #install).

这意味着应用程序的主dex应该包含在类加载器修补之前可能可访问的所有类.

如果您的应用程序代码在成功修补应用程序类加载器之前尝试引用打包在一个辅助dex文件中的类,您将收到java.lang.ClassNotFoundException.

我在这里记录了插件如何决定应该在main-dex中打包哪些类.
如果这些类引用的方法总数超过65,536限制,则构建将失败并显示Too many classes in --main-dex-list, main dex capacity exceeded错误.

我可以想到这个问题的三种可能的解决方案:

  1. (最简单的解决方案,但不适合大多数应用程序)将minSdkVersion更改为21.
  2. 收缩应用程序代码.之前曾多次讨论过这个问题(见这里这里).
  3. 如果以上解决方案都不适合您,您可以尝试使用我的解决方法解决此问题 - 我正在修补Android gradle插件,以便不在main dex中包含Activity类.这有点hacky,但对我来说效果很好.

还有一个问题,在关于此错误的Android bug跟踪系统.希望工具团队尽快提供更好的解决方案.


更新(2016年4月27日)

Gradle插件2.1.0版允许过滤main-dex列表类.
警告:这是使用不支持的api,将来会被替换.

例如,要排除您可以执行的所有活动类:

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
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您还可以检查演示此问题的示例项目(并应用上述过滤).


更新2(2016年7月1日)

Gradle插件的2.2.0-alpha4版本(使用build-tools v24)最终通过将multidex keep list减少到最小来解决了这个问题.
不应再使用2.1.0中不支持(和未记录)的过滤器.我已经更新了我的示例项目,演示了现在没有任何自定义构建逻辑的构建成功.


Dmi*_*sev 22

解决此问题的另一种方法是从主DEX文件中删除带有Runtime批注的类:

android {

    dexOptions {
        keepRuntimeAnnotatedClasses false
    }

}
Run Code Online (Sandbox Code Playgroud)

这对于使用依赖注入框架的应用程序特别有用,因为即使Dagger注释通常也保留在运行时.