Strategies for managing Gradle dependencies
You choose how to manage the dependencies!

In the life of an Android Developer, library dependencies are like heart of the project. These library dependencies grow as the project grows because our modules, functionalities and requirements grows. So it becomes somewhat challenging to maintain them in the best way possible and have them available to all the modules in the project.
Strategy 1: So let’s start with the default way of managing the dependencies: adding the dependencies in project’s app.gradle
dependencies {
implementation 'androidx.fragment:fragment-ktx:1.3.6'
implementation 'androidx.activity:activity-ktx:1.3.1'
}
Well this works fine as long as you’re having single module project and minimal nine to ten libraries. Beyond that this doesn’t seem to be an optimized way because once you enter into multi module arena you’ll find that
- Different modules uses same libraries and then managing the version upgrade in them becomes hectic.
- One may get lost in finding a library among the bundle of libraries.
So, Let’s try another approach.
Strategy 2: In this strategy we’ll try to segregate our dependencies in such a way that we can easily group them by category or any other way we find comfortable with. We’ll create a dependencies.gradle in our main project and inside it we’ll create extensions under the keyword ext as follows
ext {
androidX = [
compat : 'androidx.appcompat:appcompat:1.3.1',
recycler : 'androidx.recyclerview:recyclerview:1.3.1',
cardview : 'androidx.cardview:cardview:1.3.0',
]
network = [
retrofit : 'com.squareup.retrofit2:retrofit:2.9.0',
]
di = [
dagger : 'com.google.dagger:dagger:2.40.5',
]
....
}
As we see that now we’ve grouped our dependencies by type. Now we’ve androidX, dataConverters, network, di and so on. Now what if we’ve same version for multiple libraries? We can create constants as
def CompatVersion = '1.3.1'
def DaggerVersion = '2.40.5'
def NetworkVersion = '2.9.0'
Now we can reference these constants as
compat : 'androidx.appcompat:appcompat:$CompatVersion'
So how do we reference these extensions in our app.gradle? For that we first provide the reference to dependencies.gradle as follows
apply from: '../dependencies.gradle'
Now we can add these grouped dependencies using the extension from project object as follows
dependencies {
implementation project.ext.androidX.values()
implementation project.ext.ktx.values()
implementation project.ext.dataConverters.values()
implementation project.ext.network.values()
implementation project.ext.di.values()
}
Yeah! Now this seems better and manageable. Although I didn’t find any major issue with this approach but it doesn’t show your dependencies in the project setting module.

Strategy 3: If you want a more precise way of having your dependencies then use Version Catalogs. As per Gradle, definition of version catalog is as follows

A version catalog is a list of dependencies, represented as dependency coordinates, that a user can pick from when declaring dependencies in a build script.
Benefits to version catalog are
- enables auto completion in IDE
- centralization of dependencies for all modules
- grouping of dependencies
Result of Version catalog looks like
dependencies {
implementation libs.core.ktx
implementation libs.bundles.compose
implementation libs.bundles.lifecyle
}
Isn’t cool? Let’s look at the setup for this way of management.
If you’re using Gradle version between 7.0 and 7. 4 then you can enable this feature by adding the below line in settings.gradle
enableFeaturePreview("VERSION_CATALOGS")
You’ll have a resolution management section in settings.gradle as
dependencyResolutionManagement { }
Now, we can define our dependencies under the versionCatalogs -> libs
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
versionCatalogs {
libs {
}
}
}
We are provided with many options in version catalogs and we can use them as per our comfort in the project.
Define a version for dependency
version('compose','1.4.0')
Define the dependency
library('compose-activity','androidx.activity','activity-compose').versionRef('compose')
So our settings.gradle would look like something as
We can group dependencies using the bundle keyword. We can even define a plugin as
plugin('jmh', 'me.champeau.jmh').version('0.6.5')
There is one last important section we should discuss is when you want to share your dependencies across projects. We can do this using TOML(Tom’s Obvious Minimal Language) file format.
TOML format contains four sections namely [versions], [libraries], [bundles] and [plugins]. For example the above libs module can be defined in TOML as
[versions]
compose = "1.4.0"
composeui = "1.1.1"
[libraries]
compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose" }
compose-ui = { module = "androidx.compose.ui:ui", version.ref = "composeui" }[bundles]
compose = ["composeui", "<other libraries ref>"]
[plugins]
jmh = { id = "me.champeau.jmh", version = "0.6.5" }
And we can import this in our gradle as
from(files("../libs.versions.toml"))
By default, the
libs.versions.toml
file will be an input to thelibs
catalog. It is possible to change the name of the default catalog, for example if you already have an extension with the same name
dependencyResolutionManagement {
defaultLibrariesExtensionName.set('projectLibs')
}
And there are many more options which Version Catalogs provides. You can explore here if you want to know more about it.
You can choose whichever way suits your project but do give a try to version cataloging. Also if you are using some other way of managing dependencies then do share.
Hope this would be helpful.
Until next time!!
Cheers!