web-dev-qa-db-fra.com

Trop de classes dans --main-dex-list, capacité dex principale dépassée

J'essaie d'exécuter des cas de test d'instrumentation mais j'obtiens l'erreur ci-dessous lors de la conversion dex EXCEPTION INATTENDUE DE HAUT NIVEAU:

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

Comment résoudre ce problème en gradle?

26

Comprenons d'abord le problème:

Sur les appareils pré-Lollipop, seul le dex principal est chargé par le framework. Pour prendre en charge les applications multi-dex, vous devez patcher explicitement le chargeur de classe d'application avec tous les fichiers dex secondaires (c'est pourquoi votre classe Application doit étendre la classe MultiDexApplication ou appeler MultiDex # install =).

Cela signifie que le dex principal de votre application doit contenir toutes les classes potentiellement accessibles avant l'application de correctifs au chargeur de classe.

Vous recevrez Java.lang.ClassNotFoundException si votre code d'application essaie de référencer une classe qui a été empaquetée dans l'un de vos fichiers dex secondaires avant de patcher avec succès le chargeur de classe d'application.

J'ai documenté ici comment le plugin décide quelles classes doivent être empaquetées dans main-dex.
Si le nombre total de méthodes référencées par ces classes dépasse la limite de 65 536, la génération échouera avec Too many classes in --main-dex-list, main dex capacity exceeded Erreur.

Je peux penser à trois solutions possibles à ce problème:

  1. (La solution la plus simple, mais ne convient pas à la plupart des applications) Modifiez votre minSdkVersion en 21.
  2. Rétrécissez votre code d'application. Cela a été discuté plusieurs fois auparavant (voir ici et ici ).
  3. Si aucune des solutions ci-dessus ne fonctionne pour vous, vous pouvez essayer d'utiliser ma solution de contournement pour ce problème - je corrige le Android plugin gradle pour ne pas inclure les classes d'activité en principal dex. C'est un peu hacky, mais ça marche bien pour moi.

Il y a n problème dans Android bug tracker concernant cette erreur. J'espère que l'équipe Tools fournira bientôt une meilleure solution.


Mise à jour (27/04/2016)

La version 2.1.0 du plugin Gradle permet de filtrer les classes de liste main-dex.
Attention: ceci utilise une API non prise en charge qui sera remplacée à l'avenir.

Par exemple, pour exclure toutes les classes d'activités, vous pouvez:

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
        }
      }
    }
  }
}

Vous pouvez également vérifier mon exemple de projet qui illustre ce problème (et applique le filtrage ci-dessus).


Mise à jour 2 (01/07/2016)

La version 2.2.0-alpha4 du plugin Gradle (avec build-tools v24) résout enfin ce problème en réduisant la liste de conservation multidex au minimum .
Le filtre non pris en charge (et non documenté) de 2.1.0 ne devrait plus être utilisé. J'ai mis à jour mon exemple de projet , démontrant que la construction réussit maintenant sans aucune logique de construction personnalisée.

23
Alex Lipov

Une autre façon de résoudre ce problème consiste à supprimer les classes avec des annotations d'exécution du fichier DEX principal:

Android {

    dexOptions {
        keepRuntimeAnnotatedClasses false
    }

}

Cela est particulièrement utile pour les applications qui utilisent des infrastructures d'injection de dépendances, car même pour les annotations Dagger, elles sont généralement conservées lors de l'exécution.

22
Dmitry Zaytsev