Résolution de dépendances de SDK sur Android

Le problème de dépendances se pose pour les bibliothèques ou les packages partagés sur lesquels plusieurs autres packages disposent de dépendances, mais dépendent de versions différentes et incompatibles des packages partagés. Gradle a facilité la vie des développeurs Android : ajoutez simplement une dépendance à build.gradle et la bibliothèque requise sera intégrée de façon transparente à la version. Mais qu’arrive-t-il si deux dépendances s’appuient sur des versions différentes de la même bibliothèque ?

Gradle peut résoudre automatiquement les problèmes de dépendance si les deux dépendances appartiennent à la même configuration, par exemple la configuration de l’application. Exemple : le SDK Audience Network pour Android dépend de la bibliothèque exoplayer. Si vous devez ajouter une version différente de la bibliothèque exoplayer, Gradle choisira la version la plus récente à inclure. Mais si les deux dépendances appartiennent à des configurations différentes (par exemple, l’application et le test), Gradle déclenchera alors une erreur. Nous examinerons ci-dessous les différents conflits qui peuvent survenir et leurs solutions.

Conditions requises

Assurez-vous d’avoir terminé le Guide de démarrage et le guide Démarrer avec Android avant de commencer.

Conflits de dépendances

1 : bibliothèques partagées dans des dépendances appartenant à la même configuration

2 : bibliothèques partagées dans des dépendances appartenant à différentes configurations

Solutions aux conflits de dépendances

1 : laisser Gradle résoudre les problèmes de dépendances à votre place

2 : exclure une version spécifique des dépendances

3 : définir explicitement la bibliothèque en conflit dans Gradle

4 : forcer la résolution de la bibliothèque



Conflits de dépendances

1 : bibliothèques partagées dans des dépendances appartenant à la même configuration

Prenons l’exemple suivant. Les deux dépendances figurant dans le code ci-dessous partagent la même configuration et disposent d’une dépendance interne sur la bibliothèque org.hamcrest:hamcrest-core. Comme les deux dépendances utilisent différentes versions de la même bibliothèque en interne, c’est la version la plus récente qui sera incluse dans la compilation. Le résultat de la synchronisation Gradle indiquera clairement qu’elle mettra à jour la bibliothèque hamcrest de la version 1.1 à 1.3 dans la compilation finale.

// Depends on version 1.3 of org.hamcrest:hamcrest-core
androidTestImplementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19'  

2 : bibliothèques partagées dans des dépendances appartenant à différentes configurations

Si les deux dépendances appartiennent à des configurations différentes (par exemple, la configuration de l’application et la configuration test), Gradle déclenchera une erreur. Prenons l’extrait de code suivant. La première dépendance appartient à la configuration de l’app, tandis que la deuxième appartient à la configuration test. Ainsi, lors de la compilation du projet, la dépendance entraînera un échec avec une exception.

Lors des tests d’instrumentation, l’APK principale et l’APK de test partagent le même chemin de classe. La compilation Gradle échouera si l’APK principale et l’APK de test utilisent la même bibliothèque (par exemple, Guava), mais dans différentes versions. Si Gradle ne comprend pas cela, votre application pourrait se comporter différemment pendant les tests et l’exécution normale (ce qui inclue un plantage dans l’un des cas).

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 

Solutions aux conflits de dépendances

1 : laisser Gradle résoudre les problèmes de dépendances à votre place

Cette approche est la plus simple, mais se limite aux dépendances appartenant aux mêmes configurations. Par exemple, votre module dépend explicitement d’une version spécifique d’ExoPlayer. Or, le SDK Audience Network inclut déjà une autre version d’ExoPlayer. Par défaut, c’est la version la plus récente qui sera incluse dans la compilation. Avec l’extrait de code ci-dessous, le résultat de la synchronisation Gradle indiquera clairement qu’elle mettra à jour la bibliothèque ExoPlayer de la version r2.4.2 à 2.7.3 dans la compilation finale.

implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.3'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation 'com.facebook.android:audience-network-sdk:4.28.1' 

2 : exclure une version spécifique des dépendances

Dans la plupart des situations, les développeurs souhaiteront avoir le contrôle sur la version de la bibliothèque qui sera finalement incluse dans la compilation. Vous êtes autorisé à configurer build.gradle pour ce faire. Par exemple, si votre développeur préfère la version inférieure ExoPlayer r2.4.0 au lieu de la version r2.4.2 dans le SDK Audience Network, vous pouvez alors exclure ce module de la déclaration de dépendance audience-network-sdk. Le tag d’exclusion s’applique également aux dépendances appartenant à des configurations différentes.

Dans un véritable projet, de nombreuses dépendances dépendront de différentes versions de la même bibliothèque Dans ce cas, et pour chaque dépendance, vous devrez configurer le tag d’exclusion de manière à inclure la version attendue de la bibliothèque.

Scénario 1 : dépendances appartenant à la même configuration
implementation 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation ('com.facebook.android:audience-network-sdk:4.28.1') {
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-core'
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-dash'
}
Scénario 2 : dépendances appartenant à des configurations différentes
// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation ('org.mockito:mockito-core:1.10.19'){
    exclude group: 'org.hamcrest', module:'hamcrest-core'
}

3 : définir explicitement la bibliothèque en conflit dans Gradle

Il s’agit là d’un moyen plus propre de résoudre les conflits entre dépendances appartenant à des configurations différentes. Dans ce cas, nous devons mentionner explicitement la version de la bibliothèque que nous souhaitons inclure dans la compilation finale pour l’une ou l’autre des configurations.

Cette approche est une approche plus propre de la résolution des conflits. L’inconvénient de cette méthode est que lors de la mise à jour des dépendances, telles que junit et mockito, le développeur doit également mettre à jour la bibliothèque en conflit.

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 
// Explictly mention that include version 1.3 of org.hamcrest:hamcrest-core
androidTestCompile 'org.hamcrest:hamcrest-core:1.3' 

4 : forcer la résolution de la bibliothèque

Voici un autre moyen de résolution du conflit dans lequel plutôt que d’effectuer une déclaration de bibliothèque pour une configuration, nous la forçons pour toutes les configurations. Dans notre exemple ci-dessous, vous pouvez ajouter votre propre « stratégie de résolution » au build.gradle de niveau module afin qu’elle force l’inclusion de la version spécifique du package, indépendamment du fait que les dépendances appartiennent à la même configuration ou à des configurations différentes.

Cette approche doit être utilisée avec une certaine prudence. Dans notre premier exemple, si audience-network-sdk est mis à jour et que ces bibliothèques mettent à jour la version d’exoplayer-core et les bibliothèques exoplayer-dash, nous forcerions toujours l’utilisation d’une version antérieure. Bien que le scénario reste vrai dans la deuxième solution, avec cette approche, nous forçons la version de la dépendance sur toutes les configurations plutôt que sur une seule configuration.

Scénario 1 : dépendances appartenant à la même configuration
android {
    configurations.all {
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
    }
}
Scénario 2 : dépendances appartenant à des configurations différentes
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

Étapes suivantes

Ressources supplémentaires

Guide de démarrage

Guide technique de démarrage avec Audience Network

Référence concernant l’API

Référence concernant le SDK Facebook pour iOS