Utilisation Gradle pour diviser les bibliothèques externes dans des fichiers dex séparés pour résoudre Android Dalvik 64K méthodes limit

Existe-t-il un moyen proper/easy de résoudre la limite des méthodes 64k en utilisant Gradle?

Je veux dire une tâche de Gradle personnalisée pour utiliser des jars pré-déxés pour créer des fichiers dex séparés, au lieu d'un seul classes.dex.

Merci

Ivan

État Actuel

Actuellement, je suis aux prises avec GMS: il apporte des méthodes 20k pour utiliser L'analyse. J'utilise Proguard pour dépouiller ce qui n'est pas nécessaire, mais quand même... 72k méthodes et comptage...

Je peux diviser classes.dex en deux fichiers en utilisant dx le paramètre --multi-dex. Je l'ai réalisé manuellement en éditant

sdk/build-tools/android-4.4W/dx

Et éditer la dernière ligne comme ceci:

exec java $javaOpts -jar "$jarpath" --multi-dex "$@"

Mon fichier APK contient maintenant __classes.dex__ and __classes2.dex__.

J'essaie de charger dynamiquement le deuxième fichier avec quelques méthodes:

Malheureusement toujours pas de chance. J'espère vraiment que certains gourou Google / Facebook / Square peuvent fournir un bon solution.

22
demandé sur Ivan Morgillo 2014-05-12 20:21:35

4 réponses

Mise à jour pour Android Gradle plugin 2.2.0: Il n'est plus possible d'accéder à la tâche dex, mais en échange additionalParameters a été introduit dans le cadre de dexOptions. Utilisez-le comme

android {
  dexOptions {
    additionalParameters += '--minimal-main-dex'
    // additionalParameters += '--main-dex-list=$projectDir/<filename>'.toString()
    // additionalParameters += '--set-max-idx-number=55000'
  }
}

Mise à jour pour le plugin Android Gradle 0.14.0: Il existe maintenant un support multi-dex direct via la nouvelle directive multiDexEnabled true (nécessite build-tools 21.1.0, support repository revision 8 et Android Studio 0.9).

Réponse originale: depuis Gradle Android plugin 0.9.0 vous en fait peut passer le --multi-dex à dx en ajoutant ceci à votre application de build.gradle fichier:

afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        if (dx.additionalParameters == null) {
            dx.additionalParameters = ['--multi-dex']
        } else {
            dx.additionalParameters += '--multi-dex'
        }

        // Add more additional parameters like this:
        dx.additionalParameters += '--main-dex-list=class-list.txt'
        dx.additionalParameters += '--minimal-main-dex'
    }
}

Jusqu'à présent pour le créer Il plusieurs fichiers dex. Pour réellement utiliser les multiples fichiers dex, jetez un oeil à https://github.com/casidiablo/multidex (qui est une fourchette de la prochaine bibliothèque de support MultiDex de Google).

24
répondu sschuberth 2017-06-13 08:20:19

Dans le cas où gms était votre problème et que vous utilisez gradle

À partir de la version 6.5 de gms, vous pouvez choisir des bibliothèques D'API individuelles

Par exemple pour inclure uniquement L'API Maps:

compile 'com.google.android.gms:play-services-maps:6.5.87'

Et voici la liste complète :

      com.google.android.gms:play-services-base:6.5.87
      com.google.android.gms:play-services-ads:6.5.87
      com.google.android.gms:play-services-appindexing:6.5.87
      com.google.android.gms:play-services-maps:6.5.87
      com.google.android.gms:play-services-location:6.5.87
      com.google.android.gms:play-services-fitness:6.5.87
      com.google.android.gms:play-services-panorama:6.5.87
      com.google.android.gms:play-services-drive:6.5.87
      com.google.android.gms:play-services-games:6.5.87
      com.google.android.gms:play-services-wallet:6.5.87
      com.google.android.gms:play-services-identity:6.5.87
      com.google.android.gms:play-services-cast:6.5.87
      com.google.android.gms:play-services-plus:6.5.87
      com.google.android.gms:play-services-appstate:6.5.87
      com.google.android.gms:play-services-wearable:6.5.87
      com.google.android.gms:play-services-all-wear:6.5.87
8
répondu idanakav 2014-12-09 19:23:58

Un exemple de partitionnement de projet et de chargement de différents fichiers dex peut être trouvé ici:

Https://code.google.com/p/android-custom-class-loading-sample/

EDIT: pour Gradle vous avez déjà une réponse

Chargement de classe personnalisée dans Dalvik avec Gradle (système de Nouvelle Construction Android)

2
répondu kikoso 2017-05-23 10:29:49

Je suis le responsable de https://github.com/creativepsyco/secondary-dex-gradle/ et je suis un gradle n00b, donc j'ai choisi le chemin des scripts BASH bien que je pense que cela peut être fait directement dans le fichier de construction. Ou peut être refactorisé pour fonctionner en tant que plugin, je pourrais le faire quand je suis à la hauteur des termes avec Gradle. Voici la raison de ma logique.

Pour comprendre comment diviser le DEX, vous devez connaître l'ordre des tâches du système de construction. Si vous utilisez gradle alors vous devez savoir que il y a une série de tâches injectées dans le cycle de construction.

Par exemple:

:sdk:processReleaseJavaRes UP-TO-DATE
:sdk:packageReleaseJar
:sdk:compileReleaseNdk UP-TO-DATE
:sdk:packageReleaseJniLibs UP-TO-DATE
:sdk:packageReleaseLocalJar UP-TO-DATE
:sdk:packageReleaseRenderscript UP-TO-DATE
:sdk:packageReleaseResources UP-TO-DATE
:sdk:bundleRelease
:app:prepareComAndroidSupportAppcompatV71910Library UP-TO-DATE
:app:prepareComFacebookAndroidFacebook3141Library UP-TO-DATE
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:compileDebugJava
:app:preDexDebug
:app:dexDebug
:app:processDebugJavaRes UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageDebug
:app:zipalignDebug
:app:assembleDebug

Pour faire Dexing, vous devriez pouvoir injecter votre tâche personnalisée entre les tâches Dex* et process*. Si vous pouvez le faire, alors plusieurs DEXing devient facile.

Le script Bash ici fait essentiellement cela, si vous examinez la tâche de débogage, il sera essentiellement:

  • obtenez le fichier JAR de la bibliothèque à dex, Généralement il est spécifique à la construction et existe dans le exploded-aar dossier pour les bibliothèques Android et exécutez L'outil Dex dessus
  • Copiez-le dans le dossier assets, qui existe dans le dossier libs final à empaqueter dans l'application
  • toutes les ressources de la bibliothèque, etc., sont déjà fusionnées, ce qui signifie qu'il est nécessaire de décompresser et de compresser à nouveau le fichier.

Dans le script de construction gradle

 // For Debug simply remove the library from getting dex and create it
                //----------------------- Extra Debug Step ----------------//
                def libraryFiles = new ArrayList<?>()
                def secondaryFile = new ArrayList<?>()

                variant.dex.libraries.each {
                    File file ->
                        if (!file.absolutePath.contains("lib/unspecified/classes.jar")) {
                            libraryFiles.add(file)
                        } else {
                            secondaryFile.add(file)
                        }
                }
                variant.dex.libraries = libraryFiles
                //----------------------- Extra Debug Step ----------------//

                packagingTask.dependsOn variant.javaCompile
            }

Cela supprime manuellement la bibliothèque d'être déxée, de sorte qu'elle peut être générée via le script bash.

Je pense que vous pouvez comprendre la dexing pendant le processus de libération de la même manière. L'autre chose importante à noter est que la tâche Proguard est contrôlée par Android gradle plugin et vous ne pouvez pas changer beaucoup à ce sujet. Problème avec les règles Proguard:

  • chaque passage de proguard est différent, nous ne voulons pas nous retrouver dans une situation où nos 2 Dex ont des mappages proguard différents
  • cela nous laisse dans une situation où nous ne pouvons pas proguard nos bibliothèques, mais ce n'est pas vraiment souhaitable.
  • doit générer le fichier DEX après proguard pour s'assurer que les mappages sont les mêmes. Gradle n'a aucun support pour fusionner des actifs après Proguard (nous voulons placer les fichiers dex dans le dossier assets)

L'autre morceau important de code réside dans SecondaryDex.java {[5] } qui charge essentiellement le deuxième fichier dex et injecte le chemin du fichier DEX dans le chemin de la classe d'exécution. Vous pouvez optimiser cela et simplement injecter le chemin au lieu de lire le fichier DEX chaque fois que l'application est reprendre.

J'ai fait l'expérience secondaire Dex sur les services Google Play (qui ajoute des méthodes 20K) et j'ai pu me séparer dans un fichier DEX séparé. De cette façon, mon fichier DEX principal n'est pas affecté par le gonflement des services Google Play.

Pour comprendre comment fonctionne le cycle de tâches Gradle, vous pouvez vous référer au BasePlugin.groovy source, vous pouvez voir qu'il est difficile de contrôler certains aspects jusqu'à ce qu'il existe une API appropriée pour accéder aux objets variantes et aux tâches de construction.

2
répondu redDragonzz 2014-07-11 07:40:10