Android 101: Gradle dependencies
In Gradle, dependencies are declared as closure in dependencies
block.
dependencies {
implementation 'com.squareup.okhttp3.okhttp:3.9.1`
}
Dependency tree can be displayed via ./gradlew dependencies
task. This will display all direct and transitive dependencies.
+--- com.squareup.okhttp3:okhttp:3.9.1 : Direct artifact
\--- com.squareup.okio:okio:1.6.0 : Transitive dependency
Packaging type
All artifacts have a packaging type e.g. jar, aar. Author of the artifact could specify the default packaging type in POM before publishing. If it is not specified in POM, then the default type will be jar for downloading.
Jar (Java Archive): A packaging file format that is used to aggregate and zip java class files.
Aar (Android Archive): Same as jar, additionally includes android resources such as layouts, drawables and AndroidManifest file.
Downloading an artifact with the default packaging type (Recommended)
If the packaging type is not specified explicitly when declaring dependency as the example below, artifacts will be downloaded with the default packaging type that’s specified in POM file.
dependencies {
implementation "com.squareup.okhttp3:okhttp:3.9.1"
implementation "com.android.support:percent:26.0.0"
}
Gradle will download the artifacts for okhttp
as jar and for percent
as aar. Author of percent
declared the packacing type as aar
in POM file, therefore Gradle will use this default packaging type to download the artifact.
+--- com.squareup.okhttp3:okhttp:3.9.1
| \--- com.squareup.okio:okio:1.6.0
\--- com.android.support:percent:26.0.0
\--- com.android.support:support-compat:26.0.0
\--- com.android.support:support-annotations:26.0.0
Downloading an artifact by specifying packaging type
Packaging type can be explicitly specified by appending @type
notation.
dependencies {
implementation "com.squareup.okhttp3:okhttp:3.9.1@jar"
implementation "com.android.support:percent:26.0.0@aar"
}
Artifacts are downloaded as jar
for “okhttp and as aar
for “percent”.
+--- com.squareup.okhttp3:okhttp:3.9.1
\--- com.android.support:percent:26.0.0
But the dependency tree is slightly different now, as a result transitive dependencies are not downloaded. Gradle downloads only the main artifact without transitive dependencies when this notation is used. It is also called artifact-only notation.
There are a few ways to resolve this issue:
- Using
transitive=true
and explicitly tell Gradle to download the transitive dependencies.
dependencies {
implementation("com.squareup.okhttp3:okhttp:3.9.1@jar") {
transitive true
}
implementation("com.android.support:percent:26.0.0@aar"){
transitive true
}
}
- Gradle will resolve the issue automatically if another direct/indirect dependency have the same transitive dependencies.
For example, missing dependencies for percent
will be resolved through recyclerview-v7
automatically.
dependencies {
implementation("com.android.support:percent:26.0.0@aar")
implementation "com.android.support:recyclerview-v7:26.0.0"
}
- Each missing dependency is declared explicitly one by one.
dependencies {
implementation("com.squareup.okhttp3:okhttp:3.9.1@jar")
implementation("com.android.support:percent:26.0.0@aar")
// transitive dependencies are declared one by one implementation "com.squareup.okio:okio:1.6.0"
implementation "com.android.support:support-compat:26.0.0"
}
What if there is no artifact with the given packaging type?
Gradle will try to download the artifact but ultimately it’ll fail because it doesn’t exist in the repository for the specified packaging type.
dependencies {
implementation "com.squareup.okhttp3:okhttp:3.9.1@aar"
}
Output with error:
Failed to resolve: okhttp
Excluding specific transitive dependencies
Specific transitive dependencies can be excluded as in the situation of conflict.
Following example shows how to remove support-annotations
transitive dependency from percent
library.
dependencies {
implementation("com.android.support:percent:26.0.0") {
exclude group:'com.android.support',module:'support-annotations'
}
}
group
can be omitted if module is unique
dependencies {
implementation("com.android.support:percent:26.0.0") {
exclude module:'support-annotations'
}
}
Observe that support-annotations
no longer exists in the dependency tree.
+--- com.android.support:percent:26.0.0
\--- com.android.support:support-compat:26.0.0
Transitive dependency resolution strategy
Different artifact versions might be declared at the same time. Gradle will try to resolve it by choosing the latest version.
In the following example, a newer version of support-annotations
is declared explicitly. percent
also has the same dependency with an older version implicitly.
dependencies {
implementation "com.android.support:support-annotations:27.0.2"
implementation "com.android.support:percent:26.0.0"
}
Observe that transitive dependency indicates the latest version.
+-- com.android.support:support-annotations:27.0.2
\-- com.android.support:percent:26.0.0
\-- com.android.support:support-compat:26.0.0
\-- com.android.support:support-annotations:26.0.0 -> 27.0.2
What happens if the version has qualifier such as 26.0.0 vs 26.0.0-alpha?
I did some testing and it seems Gradle also uses the same version comparison as maven. For the complete list of how version ordering works, check maven version order specification.