主dex列表中的类太多,已超过主dex容量限制。

28

我正在尝试运行仪器测试用例,但在 dex 转换过程中遇到以下错误 UNEXPECTED TOP-LEVEL EXCEPTION:

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中解决这个问题?


你可能正在使用Google Play服务。如果你在使用Android Studio,尽量只使用你需要的子组件(如广告、游戏等),而不是一次性全部使用。这是一个已知的问题,短期内几乎不可能得到解决。问题在于Dalvik只支持65535(2^16-1)个类。一旦所有设备都使用本地ART,这将不再是一个问题。 - GiantTree
3个回答

25

首先,让我们了解这个问题:

在 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”错误。

我想到了三种可能的解决方案:

  1. (最简单的解决方案,但不适用于大多数应用程序)将 minSdkVersion 改为 21。
  2. 缩小应用程序代码。这在以前已经讨论过(请参见此处此处)。
  3. 如果以上解决方案都不适用于您,则可以尝试使用我的解决方法 - 我正在修补 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中的过滤器。我已经更新了我的示例项目,演示构建现在可以在没有任何自定义构建逻辑的情况下成功。


嗨,我也不得不使用相同的补丁。但是这个问题的根本原因是什么?有什么办法可以避免这种情况吗? - Sreenivas Karthikeyan
我已经编辑了我的回答,请告诉我是否还有其他问题。 - Alex Lipov
我没有使用Android Studio。我直接在Gradle上运行。Gradle clean assembleDebug。我为debug buildType启用了proguard。发布版本即使在2.0.0中也可以正常工作,但是debug版本会失败。 - Sreenivas Karthikeyan
我明白了。你能否比较一下发布版和调试版(也许还有v2.0发布版和v1.3.1发布版之间)的app/build/intermediates/multi-dex/{variant}/maindexlist.txt文件呢?该文件控制哪些类将被包含在主dex中。 - Alex Lipov
对于调试版本,1.3.1的maindexlist文件中有几个名称为“-keep class classname”的条目,在2.0.0中缺失。除此之外没有其他差异。另外一个观察结果是,1.3.1和2.0.0都删除了相同数量的类。 - Sreenivas Karthikeyan
显示剩余6条评论

24

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

android {

    dexOptions {
        keepRuntimeAnnotatedClasses false
    }

}

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


谢谢!这很有帮助。我已经列出了以下dexOptions: additionalParameters += '--minimal-main-dex' additionalParameters += '--set-max-idx-number=50000' - hcpl
你好!你是否曾因为 keepRuntimeAnnotatedClasses false 而遇到任何问题或崩溃? - aleien
@aleien 没有,一切都很好。 - Dmitry Zaytsev

-3

你有两个选择。

  1. 使用ProGuard来减少方法数量
  2. 使用multidex功能

我的建议-选择ProGuard,它不需要对源代码进行任何更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接