Photo by Luis Villasmil on Unsplash

A Simple Way To Improve Your Android Build.Gradle Files

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!

The Problem

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, ext.libraries and ext.libPacks.

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' ]

The 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 implementation or 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 implementation or testImplementation because that detail is defined in the objects we created.

Step by Step Solution

  • Create a libraries.gradle file in your root project folder
  • In your project-level gradle, write apply from: 'libraries.gradle' above the buildscript block
  • Declare your dependencies in libraries.gradle, modeling them as so:
  • In your module’s build.gradle file in the dependencies block, all you need to do is write libPack.retrofit or libPack.glide
Module’s build.gradle with simplified library syntax

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!