From
https://www.devsbedevin.com/android-understanding-gradle-dependencies-and-resolving-conflicts/
My latest post discussed gradle'sdependencyInsighttask. let's dive deeper into dependencies, direct and transitive, and gradle's dependency tools.
Transitive dependency is an implied dependency, allowing your project to depend on libraries that depend on other libraries. The result is a dependency tree. These trees tend to get complex as your project requires more and more libraries to compile.
A direct, or "first level" dependency is one that you the developer explicitly import.
We'll get back to transitive dependencies in a second. For now let's look at a project's entire dependency tree.
Invokinggradle dependencies --configuration compileresults in something like this:
Already we can learn that this project has a lot of dependency conflicts. Those are marked by
{library name}:{required version} -> {actual version}
It is very common for for different modules and libraries to share the same dependencies. Who doesn't useapache-commonsandGSon Huh folks Am I right This guy knows what I'm talking about.
A problem arises when the dependency is the same, but each module or library expects a different version. This causes the project to contain multiple versions of the same class, and in turn causes the Dex tool to fail with the following error:
com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry:
If we didn't have gradle's transient dependency management, we would have to manually keep track of each dependency and resolve version conflicts by hand. This becomes harder the larger your project is. Lucky us we have gradle to handle the headache in one of several ways:
The aforementioned strategies do not always translate directly to configuration switches.
Let's go over the basic techniques in achieving what we want:
This is achieved via theresolutionStrategyartifact.
Add the following to your main project'sbuild.gradlefile.
configurations.all { resolutionStrategy { failOnVersionConflict() } }
The following example forcesasm-all 3.3.1,commomns-io 1.4, and latestboltswhere the major version number is 1 (i.e. bolts 1.x):
configurations.all { resolutionStrategy { force 'asm:asm-all:3.3.1', 'commons-io:commons-io:1.4', 'com.parse.bolts:bolts-android:1.+' } }
Always prefer own modules:
configurations.all { resolutionStrategy { preferProjectModules() } }
Replace all instances ofcommons-iowith a custom modulemy-commons-io:
configurations.all { resolutionStrategy { dependencySubstitution { substitute module('commons-io:commons-io:2.4') with project(':my-commons-io') } } }
You may also fine grain your settings to a specific dependency on a specific project.
Adding this to your dependencies tag would add the libraryappcompat-v7but none of its own transient dependencies. You will have to make sure that all of the libraries needed dependencies are added either by adding direct dependencies yourself or by relying on transient dependencies referenced by other modules:
dependencies { compile('com.android.support:appcompat-v7:23.1.0') { transitive = false } }
The default fortransitiveistrue.
You can also exclude specific transient dependencies. The following example excludes theboltslibrary from being added, if needed byappcompat:
dependencies { compile('com.android.support:appcompat-v7:23.1.0') { exclude group: 'com.parse.bolts' } }
This forces your project to settle onboltsversion 1.1:
dependencies { compile('com.parse.bolts:bolts-android:1.+') { force = true } }
Example taken from gradle'sofficial docs:
configurations.all { resolutionStrategy { // fail eagerly on version conflict (includes transitive dependencies) // e.g. multiple different versions of the same dependency (group and name are equal) failOnVersionConflict() // prefer modules that are part of this build (multi-project or composite build) over external modules preferProjectModules() // force certain versions of dependencies (including transitive) // *append new forced modules: force 'asm:asm-all:3.3.1', 'commons-io:commons-io:1.4' // *replace existing forced modules with new ones: forcedModules = ['asm:asm-all:3.3.1'] // add dependency substitution rules dependencySubstitution { substitute module('org.gradle:api') with project(':api') substitute project(':util') with module('org.gradle:util:3.0') } // cache dynamic versions for 10 minutes cacheDynamicVersionsFor 10*60, 'seconds' // don't cache changing modules at all cacheChangingModulesFor 0, 'seconds' } }
dependencies { // We load commons-io but none of its transient dependencies compile('commons-io:commons-io:2.4') { transitive = false } // We exclude bolts altogether from appcompat's transient dependencies compile('com.android.support:appcompat-v7:23.1.0') { exclude group: 'com.parse.bolts' } // Force a specific version compile('com.parse.bolts:bolts-android:1.+') { force = true } }
To review all of your project's dependencies:
gradle dependencies --configuration CONFIGURATION_NAME
To inspect a specific dependency (more on this inthis post):
gradle -q dependencyInsight --configuration CONFIGURATION_NAME --dependency DEPENDENCY_NAME