Android 101: Gradle dependencies

Orhan Obut
AndroidPub
Published in
3 min readFeb 8, 2018

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.

Links

--

--