Déboguer les erreurs de résolution des dépendances

Lorsque vous ajoutez une dépendance, vous pouvez rencontrer des problèmes de dépendances requises par la dépendance d'origine et des conflits entre différentes versions de dépendance. Voici comment analyser votre graphique de dépendances et résoudre les problèmes courants.

Pour obtenir des conseils sur la correction des erreurs de résolution des dépendances impliquant une logique de compilation personnalisée, consultez la section Stratégies de résolution des dépendances personnalisées.

Afficher les dépendances du module

Certaines dépendances directes peuvent avoir leurs propres dépendances. Celles-ci sont des dépendances transitives. Plutôt que de vous demander de déclarer manuellement chaque dépendance transitive, Gradle les collecte et les ajoute automatiquement pour vous. Le plug-in Android pour Gradle fournit une tâche qui affiche une liste des dépendances résolues par Gradle pour un module donné.

Pour chaque module, le rapport regroupe également les dépendances en fonction de la variante de compilation, de l'ensemble de sources de test et du classpath. Voici un exemple de rapport pour le classpath d'exécution d'un module d'application dans sa variante de compilation de débogage, et pour le classpath de compilation de son ensemble de sources de test instrumenté.

debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...

debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...

Pour exécuter la tâche, procédez comme suit :

  1. Sélectionnez Afficher > Fenêtres d'outils > Gradle ou cliquez sur Gradle  dans la barre des fenêtres d'outils.
  2. Développez AppName > Tâches > Android, puis double-cliquez sur androidDependencies. Une fois que Gradle a exécuté la tâche, la fenêtre Exécuter doit afficher le résultat.

Pour en savoir plus sur la gestion des dépendances dans Gradle, consultez la section Principes de base de la gestion des dépendances dans le guide de l'utilisateur de Gradle.

Exclure les dépendances transitives

À mesure qu'une application prend de l'ampleur, elle peut accumuler un certain nombre de dépendances, y compris directes ou transitives (des bibliothèques dont dépendent les bibliothèques importées par votre application). Pour exclure les dépendances transitives dont vous n'avez plus besoin, vous pouvez utiliser le mot clé exclude, comme indiqué ci-dessous :

Kotlin

dependencies {
    implementation("some-library") {
        exclude(group = "com.example.imgtools", module = "native")
    }
}

Groovy

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

Exclure les dépendances transitives des configurations de test

Si vous devez exclure certaines dépendances transitives de vos tests, l'exemple de code présenté ci-dessus risque de ne pas fonctionner comme prévu. En effet, une configuration de test (par exemple, androidTestImplementation) étend la configuration implementation du module. Autrement dit, elle contient toujours des dépendances implementation lorsque Gradle résout la configuration.

Pour exclure les dépendances transitives de vos tests, vous devez procéder au moment de l'exécution, comme indiqué ci-dessous :

Kotlin

android.testVariants.all {
    compileConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
    runtimeConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
}

Groovy

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

Remarque : Vous pouvez toujours utiliser le mot clé exclude dans le bloc de dépendances, comme indiqué dans l'exemple de code d'origine de la section Exclure les dépendances transitives, pour omettre les dépendances transitives qui sont spécifiques à la configuration de test et ne sont pas incluses dans d'autres configurations.

Corriger les erreurs de résolution des dépendances

Lorsque vous ajoutez plusieurs dépendances à votre projet d'application, ces dépendances directes et transitives peuvent entrer en conflit. Le plug-in Android Gradle tente de résoudre ces conflits sans complications, mais certains cas peuvent entraîner des erreurs de compilation ou d'exécution.

Pour vous aider à identifier l'origine des erreurs, inspectez l'arborescence des dépendances de votre application et recherchez celles qui apparaissent plusieurs fois ou dont les versions posent problème.

Si vous ne parvenez pas à identifier facilement les dépendances en double, utilisez l'interface d'Android Studio pour rechercher les dépendances qui incluent la classe en double. Procédez comme suit :

  1. Sélectionnez Naviguer > Classe dans la barre de menu.
  2. Dans la boîte de dialogue de recherche pop-up, assurez-vous que la case Include non-project items (Inclure les éléments extérieurs au projet) est cochée.
  3. Saisissez le nom de la classe qui apparaît dans l'erreur de compilation.
  4. Inspectez les résultats pour les dépendances qui incluent la classe.

Les sections suivantes décrivent les différents types d'erreurs de résolution de dépendances que vous pouvez rencontrer, et comment les résoudre.

Corriger les erreurs de classe en double

Si une classe apparaît plusieurs fois dans le classpath d'exécution, cela produit une erreur semblable à celle-ci :

Program type already present com.example.MyClass

Cette erreur survient généralement dans l'un des cas suivants :

  • Une dépendance binaire inclut une bibliothèque que votre application inclut également en tant que dépendance directe. Par exemple, votre application déclare une dépendance directe aux bibliothèques A et B, mais la bibliothèque A inclut déjà la bibliothèque B dans son binaire.
    • Pour résoudre ce problème, supprimez la bibliothèque B des dépendances directes.
  • Votre application dispose d'une dépendance binaire locale et d'une dépendance binaire distante à la même bibliothèque.
    • Pour résoudre ce problème, supprimez l'une des dépendances binaires.

Résoudre les conflits de classpath

Au moment de résoudre le classpath de compilation, Gradle commence par résoudre le classpath d'exécution et utilise le résultat pour déterminer les versions des dépendances qui doivent être ajoutées au classpath de compilation. En d'autres termes, classpath d'exécution détermine les numéros de version requis pour les dépendances identiques des classpaths en aval.

Le classpath d'exécution de votre application détermine également les numéros de version requis par Gradle pour mettre en correspondance les dépendances dans le classpath d'exécution de l'APK de test de l'application. La hiérarchie des classpaths est décrite à la figure 1.

Figure 1 : Les numéros de version des dépendances qui apparaissent dans plusieurs classpaths doivent correspondre en fonction de la hiérarchie ci-dessus.

Un conflit peut survenir lorsque différentes versions de la même dépendance apparaissent sur plusieurs classpath (par exemple, lorsque votre application inclut une version d'une dépendance sous la configuration de dépendance implementation alors qu'un module de bibliothèque exploite une autre version de cette dépendance sous la configuration runtimeOnly).

Lors de la résolution des dépendances dans les classpaths d'exécution et de compilation, le plug-in Android Gradle 3.3.0 et ses versions ultérieures tentent de résoudre automatiquement certains des conflits de version en aval. Par exemple, si le classpath d'exécution inclut la bibliothèque A en version 2.0 tandis que le classpath de compilation inclut la bibliothèque A en version 1.0, le plug-in met automatiquement à jour la dépendance dans le classpath de compilation vers la version 2.0 de la bibliothèque A, pour éviter les erreurs.

Par contre, si le classpath d'exécution inclut la bibliothèque A en version 1.0 et que le classpath de compilation inclut la bibliothèque A en version 2.0, le plug-in ne rétrograde pas la version de la dépendance dans le classpath de compilation. Ce cas de figure produit une erreur semblable à la suivante :

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

Pour résoudre ce problème, effectuez l'une des opérations suivantes :

  • Incluez la version souhaitée de la dépendance en tant que dépendance api dans votre module de bibliothèque. Autrement dit, seul votre module de bibliothèque déclare la dépendance, mais le module de l'application aura également accès à son API de manière transitoire.
  • Vous pouvez également déclarer la dépendance dans les deux modules, mais vous devrez alors vous assurer qu'ils utilisent la même version. Envisagez de configurer des propriétés à l'échelle du projet pour vous assurer que les versions de chaque dépendance restent cohérentes dans votre projet.