Gradle can be frustrating to maintain. When I work on a project with multiple modules and a handful of third party libraries I start to feel the stress. Retrofit might release a new version and then you have a 15 minute task on your hands updating the version in each module’s
build.gradle. Or you’ll create a new module and have to Google what dependencies are required for a library you already have in another module. I may still be a Gradlepprentice, but by the end of this article we’ll be on our way to mastering this remarkable tool!
Any product you work on is likely to have third party libraries that you have to declare in your
build.gradle, but these implementation details can be rather complex on their own. Take one of the most common Android Dependency Injection libraries: Dagger. In order to use it in your project you need both the library and the annotation processor. That means each module that uses Dagger requires two lines of boilerplate to implement.
It begs the question: why can’t I just write what library I want to use in my module; and let my build system handle the other concerns?
Some Gradle Background & My Approach
Gradle is a build tool shipped with Android Studio which is controlled by scripts written in Groovy by default. We can use Groovy to define functions, variables, and utilize multiple files in order to keep the build scripts organized. A lot of the variables used in this example are maps.
Gradle also supports global variables which are defined by the prefix
ext and a variable name. In this project I used 3 global variables:
ext.versions holds the version for any libraries I need to use. Sometimes libraries can have multiple dependencies, so it can be good to label these versions very explicitly.
ext.versions = [ myLibVersion : '1.0.0' ]
ext.libraries map contains our artifacts. Whatever you would normally write after
implementation will go into this map. These artifacts can be defined as such:
myKey : "artifact.path:$versions.artifactVersion",
Using double quotes we are able to write
$versions.artifactVersion to insert the version into our artifact declaration, allowing us to edit the version once and have it update everywhere.
ext.libPacks is the abstraction that handles library packages and what action needs to be taken to import them into our project.
Using this approach can hide the details of whether it’s an
testImplementation and keeps everything centralized in one file. We make use of all the other global objects we declared above to keep each line clean.
When we use these objects in our module
build.gradle dependencies; we don’t use
testImplementation because that detail is defined in the objects we created.
Step by Step Solution
- Create a
libraries.gradlefile in your root project folder
- In your project-level gradle, write
apply from: 'libraries.gradle'above the
- Declare your dependencies in
libraries.gradle, modeling them as so:
- In your module’s
build.gradlefile in the
dependenciesblock, all you need to do is write
Gradle is one of those tools that I’m constantly trying to understand a little better, and I think this approach really simplifies things when used in multi-module projects. If you have questions or comments I’d love to hear them!
Shoutout to David and others who helped edit and review this post!