The dependency issue arises around shared packages or libraries on which several other packages have dependencies, but where they depend on different and incompatible versions of the shared packages. Gradle has made the lives of Android developers quite easy - just add one dependency in the build.gradle, and the required library is seamlessly included in the build. But what happens when two dependencies have a dependency on different versions of the same library?
Gradle is able to automatically resolve dependencies if both dependencies belong to same configuration, i.e. app configuration. For example, Audience Network Android SDK depends on "exoplayer" library. If you need to add a different version of "exoplayer" library, Gradle will choose the higher version to include. But if both the dependencies belong to different configurations, i.e app and test, then Gradle will throw an error. Below we will go through the different conflicts and solutions.
Ensure you have completed the Audience Network Getting Started and Android Getting Started guides before you proceed.
Consider the following example. Both dependencies in the below code stay in same configuration and have an internal dependency on a library "org.hamcrest:hamcrest-core". Because both dependencies are internally using different versions of the same library, the highest version gets included in the build. The output of the Gradle sync will clearly indicate that it automatically upgrades the hamcrest library version from 1.1 to 1.3 in the final build.
// 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'
If both dependencies belong to different configurations i.e app and test configuration, then Gradle will throw an error. Consider the following code snippet. The first dependency belongs to the app configuration, while the second dependency belongs to the test configuration. So, while building the project, it will fail with exception.
When instrumentation tests are run, both the main APK and test APK share the same classpath. Gradle build will fail if the main APK and the test APK use the same library (e.g. Guava) but in different versions. If Gradle didn’t catch that, your app could behave differently during tests and during normal run (including crashing in one of the cases).
// 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'
This approach is the simplest one but limited to dependencies in same configurations. For example, your module explicitly depends on a specific version of ExoPlayer. However, the Audience Network SDK already has included another version of ExoPlayer. On default, the highest version gets included in the build. With code snippet below, the output of Gradle sync will clearly indicate that it automatically upgrades the ExoPlayer library version from r2.4.2 to 2.7.3 in the final build.
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'
In most situations, developers may want to have control over which version of the library will be finally included in the build. You are allowed to configure build.gradle to make it happen. For example, if your developer prefers the lower version of ExoPlayer r2.4.0 instead of r2.4.2 in Audience Network SDK, then you can exclude that module while declaring "audience-network-sdk" dependency. The exclude tag applies to dependencies in different configurations, too.
In a real project, there will be many dependencies which will have different versions of the same library. In that case, for each and every dependency, you will need to have the exclude tag such that you can include expected version of that library.
Scenario 1: Dependencies in Same Configurationimplementation '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' }Scenario 2: Dependencies in Different Configuration
// 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' }
This is a cleaner way of resolving the conflict for dependencies from different configurations. In this case, we need to explicitly mention the version of the library which we want to include in the final build for any one of the configurations.
This approach is a cleaner approach to resolve the conflicts, but the downside is that while updating the actual dependencies like junit and mockito, the developer needs to update the conflicted library as well.
// 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'
This is another way of resolving the conflict in which instead of declaring for one configuration, we force it for all the configurations. In our example below, you can add your own "resolutionStrategy" into module level build.gradle, so it forces the specified version of package to be included regardless of dependencies in same or different configurations.
This approach should be used with some caution. In our first example, if the audience-network-sdk is updated and these libraries update the version of exoplayer-core and exoplayer-dash libraries, we would still be forcing to use a backward version. Although the scenario holds true for the second solution approach, in this approach we are forcing the dependency version on all the configurations instead on a single configuration.
Scenario 1: Dependencies in Same Configurationandroid { configurations.all { resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0' resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0' } }Scenario 2: Dependencies in Different Configuration
android { configurations.all { resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1' } }
Test ads integration with your app
Submit your app for review.
As soon as we receive a request for an ad from your app or website, we'll review it to make sure it complies with Audience Network policies and the Facebook community standards.
More Resources |
Getting Started GuideTechnical guide to getting started with Audience Network | API ReferenceFacebook SDK for iOS Reference |