Gradle dependency management

Gradle Dependency Management
If you are using single module architecture in your project, there’s no problem. But multi-module Android projects are the primary way to develop an Android App now, and it’s highly recommended to use this way to take advantage of performance improvements with Android Gradle Plugin (AGP) 3+. However, when the module size increases, we encounter a big issue - dependency management in each module.
Here we are going to introduce some solutions for Gradle dependency management.
Manual management
Not a recommended way to do, you might see there are many duplicated dependency code in different modules, for example:
Module_A/build.gradle
implementation "com.android.support:support-annotations:27.0.2"
implementation "com.android.support:appcompat-v7:27.0.2"
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
implementation "io.reactivex.rxjava2:rxjava:2.1.9"
Module_B/build.gradle
implementation "com.android.support:support-annotations:27.0.2"
implementation "com.android.support:appcompat-v7:27.0.2"
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
implementation "io.reactivex.rxjava2:rxjava:2.1.9"
This is not a good way and is painful. You will need to dive into different modules and copy-paste almost the same dependencies for each module.
Google Ext Section
This is the way recommended by Google. First of all, we need to define a project-level build.gradle and put the version and library information inside.
Project-level build.Gradle
buildscript {...}
allprojects {...}
// The following are only a few examples of the types of properties you can define.
extra["sdkVersion"] = 28
extra["supportLibVersion"] = "28.0.0"
...
After that, we need to define the module-level build.gradle file,
android {
// Use the following syntax to access properties you define at the project level: // rootProject.ext.property_name
val sdkVersion: Int by rootProject.extra
compileSdkVersion(sdkVersion)
...
}
...
dependencies {
implementation("com.android.support:appcompat-v7:${rootProject.extra["supportLibVersion"]}") ...
}
By using this way, it’s very convenient to manage all dependencies in a single file. That’s a considerable improvement.
Kotlin buildSrc
When I was at Cruse, we switched our dependency management to this way. You need to create a buildSrc module with Kotlin code to manage dependencies. The most significant benefit you can gain is leveraging the IDE completion support when importing a dependency.
So, here are steps to use this way.
-
Create a
buildSrcmodule -
Create
build.gradle.ktsfile to enable this feature.plugins { `kotlin-dsl` } -
Create your dependency file, for example, ProjectDependency.kt
object Versions {
val retrofit = "x.y.z"
}
object Libs {
val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
}
After creating those files, we can start using those defined dependencies in our module-level build.gradle files.
Gradle Version Catalog
Based on the Gradle document, central declaration of dependencies is an incubating feature. It requires the activation of the VERSION_CATALOGS :
enableFeaturePreview('VERSION_CATALOGS')
Here is an example to show how to define your version catalog:
settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
alias("groovy-core").to("org.codehaus.groovy:groovy:3.0.5")
alias("groovy-json").to("org.codehaus.groovy:groovy-json:3.0.5")
alias("groovy-nio").to("org.codehaus.groovy:groovy-nio:3.0.5")
alias("commons-lang3").to("org.apache.commons", "commons-lang3").version {
strictly("[3.8, 4.0[")
prefer("3.9")
}
}
}
}
Based on this example, you can find the rules as following:
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
version('version name', 'version code')
alias('alias-sample').to('group', 'artifact').versionRef('version name')
alias('alias.sample').to('group', 'artifact').versionRef('version name')
bundle('bundle-set', ['alias-sample', 'alias.sample'])
}
}
}
It enables you to define your alias and bundle at the top level. You can use them in different modules like, libs.alias.sample or bundle.bundle.set . Version catalogs support autocompletion in the IDE through their type-safe accessors generated by Gradle.
Gradle also supports TOML for the version catalog feature. You would need to create a file called libs.versions.toml in the gradle subdirectory of the root build, then a catalog will be automatically declared with the contents of this file. The TOML file consists of 4 major sections:
- the
[versions]section is used to declare versions that dependencies can reference - the
[libraries]section is used to declare the aliases to coordinates - the
[bundles]section is used to declare dependency bundles - the
[plugins]section is used to declare plugins
Here is an example for this file:
[versions]
groovy = "3.0.5"
checkstyle = "8.37"
[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }
[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]
[plugins]
jmh = { id = "me.champeau.jmh", version = "0.6.5" }
The usage is very similar when you define those parameters in your gradle files. For example,
// access version section
buildscript {
dependencies {
classpath "lib.x.y.z:${libs.versions.groovy.get()}"
}
}
// access a single dependency
implementation libs.groovy.core
// access a bundle
implementation libs.bundles.groovy
That’s it. Enjoy the coding.
#Android #Android/Gradle/VersionCatalog