Слишком много классов в --main-dex-list, превышена емкость основного dex

Я пытаюсь запустить контрольно-измерительные тесты, но получаю ошибку ниже при преобразовании 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?

3 ответа

Решение

Давайте сначала разберемся в проблеме:

На устройствах, предшествующих Lollipop, платформа загружает только основной dex. Для поддержки мультидексных приложений вы должны явно исправить загрузчик класса приложений всеми вторичными dex-файлами (вот почему ваш класс Application должен расширять класс MultiDexApplication или вызывать MultiDex # install).

Это означает, что основная программа вашего приложения должна содержать все классы, которые потенциально доступны до исправления загрузчика классов.

Вы получите java.lang.ClassNotFoundException, если код вашего приложения будет пытаться ссылаться на класс, который был упакован в один из ваших вторичных dex-файлов, прежде чем устанавливать исправления в загрузчик классов приложения.

Я задокументировал здесь, как плагин решает, какие классы должны быть упакованы в main-dex.
Если общее количество методов, на которые ссылаются эти классы, превышает предельное значение 65 536, сборка завершится неудачно с Too many classes in --main-dex-list, main dex capacity exceeded ошибка.

Я могу придумать три возможных решения этой проблемы:

  1. (Самое простое решение, но не подходит для большинства приложений) Измените minSdkVersion на 21.
  2. Сожмите код приложения. Это обсуждалось много раз ранее (см. Здесь и здесь).
  3. Если ни одно из вышеперечисленных решений не работает для вас, вы можете попытаться использовать мой обходной путь для этой проблемы - я исправляю плагин gradle для Android, чтобы не включать классы Activity в main dex. Это немного смешно, но у меня хорошо работает.

Существует проблема в трекере ошибок Android относительно этой ошибки. Надеемся, что команда Инструменты в ближайшее время предоставит лучшее решение.


Обновление (27.04.2016)

Плагин Gradle версии 2.1.0 позволяет фильтровать классы main-dex list.
Предупреждение: это использует неподдерживаемый 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
        }
      }
    }
  }
}

Вы также можете проверить мой пример проекта, который демонстрирует эту проблему (и применяет вышеупомянутую фильтрацию).


Обновление 2 (01.07.2016)

Версия 2.2.0-alpha4 плагина Gradle (с build-tools v24) наконец-то решает эту проблему, сводя к минимуму мультидексный контрольный список.
Неподдерживаемый (и недокументированный) фильтр из 2.1.0 больше не должен использоваться. Я обновил мой пример проекта, демонстрируя, что сборка теперь успешна без какой-либо специальной логики сборки.

Еще один способ решить эту проблему - удалить классы с аннотациями времени выполнения из основного файла DEX:

android {

    dexOptions {
        keepRuntimeAnnotatedClasses false
    }

}

Это особенно полезно для приложений, которые используют структуры внедрения зависимостей, поскольку даже для аннотаций Dagger обычно хранятся во время выполнения.

У вас есть два варианта.

  1. используйте ProGuard, чтобы сократить количество методов
  2. использовать мультидекс функцию

Мой совет - переходите на ProGuard, он требует всего лишь нулевых изменений в исходном коде

Другие вопросы по тегам