【问题标题】:Too many classes in --main-dex-list, main dex capacity exceeded--main-dex-list 中的类太多,超出主 dex 容量
【发布时间】:2017-05-24 19:58:35
【问题描述】:

我正在尝试运行仪器测试用例,但在 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 中解决这个问题?

【问题讨论】:

  • 您可能正在使用 Google Play 服务。如果您使用的是 Android Studio,请尝试仅使用您需要的子组件(如广告、游戏等),而不是一次全部使用。这是一个已知问题,在不久的将来几乎不会修复。问题是 Dalvik 只支持 65535 (2^16 - 1) 个类。一旦所有设备都使用原生 ART,这将不再是问题。

标签: android-gradle-plugin dex


【解决方案1】:

我们先来了解一下问题:

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

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

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

我已经documented here 知道插件如何决定哪些类应该打包在 main-dex 中。
如果这些类引用的方法总数超过 65,536 个限制,则构建将失败并出现 Too many classes in --main-dex-list, main dex capacity exceeded 错误。

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

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

Android 错误跟踪器中有 an issue 与此错误有关。希望工具团队能尽快提供更好的解决方案。


更新(2016 年 4 月 27 日)

2.1.0 版的 Gradle 插件允许过滤 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
        }
      }
    }
  }
}

您还可以查看我的example project,该example project 演示了此问题(并应用了上述过滤)。


更新 2(2016 年 7 月 1 日)

Gradle 插件 2.2.0-alpha4 版本(带有 build-tools v24)终于通过reducing multidex keep list to a minimum解决了这个问题。
不应再使用 2.1.0 中不受支持(且未记录)的过滤器。我更新了我的sample project,证明现在构建成功,无需任何自定义构建逻辑。

【讨论】:

  • 嗨,我也必须使用相同的补丁。但是这个问题的根本原因是什么?知道如何避免这种情况吗?
  • 我已经编辑了我的答案,如果您还有其他问题,请告诉我。
  • 非常感谢。
  • 嗨,我决定使用 proguard 来减少方法的数量。使用插件 1.3.1,我可以在没有补丁代码的情况下生成 apk。但是使用最新的 2.0.0 插件我无法这样做。我结束了同样的错误。为什么它在 1.3.1 中运行良好,但在 2.0.0 中运行良好?
  • 您可以使用我的解决方法补丁直到issue 解决。为了更好地理解为什么您在 1.3.1 和 2.0.0 版本中收到不同的结果:您是使用 Android Studio 构建还是直接从命令行使用 gradlew 构建?我问是因为如果您使用启用了 Instant Run 的 AS 2.0 进行构建 - 它会在调试构建中为您的 dex 添加大约 140 methods + 3*class#。您可以尝试运行./gradlew clean assembleRelease 并通知我结果吗?
【解决方案2】:

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

android {

    dexOptions {
        keepRuntimeAnnotatedClasses false
    }

}

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

【讨论】:

  • 谢谢!这有帮助。我已经列出了以下 dexOptions additionalParameters += '--minimal-main-dex' additionalParameters += '--set-max-idx-number=50000'
  • 您好!你有没有因为keepRuntimeAnnotatedClasses false而遇到任何问题或崩溃?
  • @aleien 不,一切都很好
【解决方案3】:

你有两个选择。

  1. 使用 ProGuard 减少方法数量
  2. 使用多索引功能

我的建议 - 使用 ProGuard,它只需要对源代码进行零更改

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 2020-02-03
    • 2018-06-23
    相关资源
    最近更新 更多