What is Gradle and why do we use it as Android developers?

At the start of a journey for an Android developer, no one pays attention to Gradle. We are mostly focused on writing Kotlin code and developing Android apps that bring great value to our customers and look as beautiful as possible.

When building them, sometimes we stumble upon a problem that is already solved by someone else on the internet and they were kind enough to share their project in a form of library, that we are able to implement in our app, and customize it to our needs. To be able to do so, we are using build.gradle file for implementing those libraries that help us build our apps, by specifying their unique package names and versions. This is probably the most common use case of our Gradle usage inside our Android projects.

At the beginning of my career I didn’t see a need for better understanding of Gradle, because for me it represented only the place where I wrote the names of the libraries I wanted to use inside my project. With that kind of approach, I didn’t have any knowledge regarding it, and I was scared to change anything inside those files, because I was afraid I would break everything.

After some time I realized that gradle files are built with code.. And I am a programmer.. So why am I avoiding it when I can learn it like any other programming language.

What an epiphany, right?

That’s why I decided to write an article regarding the gradle files inside Android project. Just so anyone can read and understand them.

Gradle

Gradle is a build tool that we use for Android development to automate the process of building and publishing apps. Now, you may think that building and running the application is quite easy; we can just press Run button in Android Studio. Well, that just triggers Gradle’s built-in tasks (functions) that are responsible for running the application on our emulator or real device. This process requires a couple of steps to be successfully completed in order for us to see the running app. These are the general steps that happen when we press Run button:

  1. Gradle reads the app’s build configuration file (build.gradle) which contains information about the dependencies, build types, etc.
  2. Gradle downloads and resolves the app’s dependencies.
  3. Gradle compiles the app’s code, which includes converting the Java or Kotlin code into bytecode. (If you are interested in knowing more about this topic, you can check my blog Process of compiling Android with Kotlin/Java code)
  4. After that, Gradle packs the compiled code and resources into an APK file. (You can check my blog if you want to see What is inside APK file)
  5. Finally, Gradle installs the APK file on the device or emulator and runs the app.

Gradle comes with a lot of built-in functionalities, but it also lets us write our own logic. It provides its own domain-specific language (DSL), which is a type of programming language that is tailored to a build automation domain, and it is based on Groovy or Kotlin for describing build scripts.

No matter what programming language you use, the gradle files are structured the same and they are built from the same parts of DSL.

Two build.gradle files and settings.gradle

Android project contains settings.gradle and two build.gradle files, from which one is placed in the root directory while the other is contained inside the app module. This may seem a bit confusing when you open your Project structure in Android layout, because they will be placed one after another, but they will contain small description next to their names in form of “project” and “app module”. For more clearance, I advise you to keep selected Project layout for your Project structure at the beginning.

Android Project structure

As you can see, apart from those two, there is also a settings.gradle file located in the root directory. Let’s take a look into it, to start our process of understanding gradle files.

settings.gradle(.kts)

settings.gradle

Given image displays the settings.gradle file written in Groovy, on the left, and same file written in Kotlin on the right.

There are two main sections inside it:

  1. Plugin management,
  2. Dependency resolution management.

Both of these sections are regular functions, but because they only receive one parameter and that parameter is in the shape of a lambda function, we are able to ignore parentheses.

The pluginManagement function contains a lambda function, as parameter, that provides a PluginManagementSpec argument which is accessible with “it” in Groovy or “this” in Kotlin language keyword (but we don’t need to specify them explicitly). Inside PluginManagementSpec we can find a new function called repositories, that contains new lambda function as parameter and it provides a RepositoryHandler argument inside. That is the place we pass the list of our repositories. Mentioned repositories, basically represent the places where Gradle will try to find plugins (the ones required by our project) and it will download the ones it finds. Later on, we will see where is the place where we need to specify the necessary plugins.

The dependencyResolutionManagement function does the similar thing as the pluginManagement one, but it provides the list of repositories where Gradle will search for dependencies that are necessary for our project.

Dependencies are all those third party libraries that we can use in our Kotlin code when developing our apps.

On the other hand plugins are similar to dependencies; the big difference is that plugins provide all the tasks (functions) that Gradle is using when building our projects. So it is basically a third party library for our gradle files.

At the bottom of settings.gradle file there is a line that specifies which modules are included in the project, so in Groovy that line of code looks like this include ‘:app’. If you come from Java\Kotlin background, there is a big chance you don’t understand this type of writing, because it seems like you want to give the value of ‘:app’ to the variable include, but there is no equal sign. So what is this thing?

include ‘:app’ is actually a regular function include, that receives one parameter of type String. Groovy has a feature that enables developers to call functions that expect one parameter without parentheses. And in Groovy you can define strings with a single quotation mark and with a double.

So the equivalent line in Kotlin would be include(”:app”), which should be much more understandable since we are coming from the background of Java/Kotlin.

build.gradle (project root)

The project-level build.gradle file is used to define global configuration and settings for the entire project. It is also used to implement dependencies and plugins on top level. By default, all configurations and settings inside our project-level build.gradle should be inherited by all the modules in the project, and build.gradle files inside modules are able to override them.

The most common things that are set inside project-level build.gradle are the plugins that are necessary for building the Android app, such as:

  1. Android Gradle,
  2. Kotlin,
  3. Hilt.

Sometimes you also have a need to add libraries that you would like to use across your modules, like:

  1. Android Support Library,
  2. Google Play Services Library.

Here is the example of project-level build.gradle that we have when we start new Android project.

build.gradle (project level)

At the top, we have buildscript and ext functions that are used for setting up our global values that we would like to use in build.gradle files inside our modules. So it’s a pretty common approach of defining the versions for all our libraries here, but it’s an optional thing to have, so we would be fine if we deleted the whole buildscript block here.

The section that is important to us is the plugins method that enables us to define our core plugins which are essential for our Android project. Mentioned plugins will be searched in the repositories set inside settings.gradle file.

The syntax for defining these plugins was a bit confusing to me, so let’s break it down.

Firstly, we call the id function and we pass one parameter as String type that represents the unique package name of the plugin we want to use. This would be equivalent to id(”com.android.application”). The next step is setting the version of the mentioned plugin; this is also a function that accepts one parameter of type String. The trick here is that the version function can only be called from PluginDependencySpec object, and that object we receive as a result of id(”…”) function. And the same applies to the apply keyword; it is also a function, just like version, but it accepts a boolean as a parameter. If we want to migrate this to Kotlin in order to improve readability of this code, the line would look like this id(“com.android.application”).version(“7.3.1”).apply(false).

build.gradle (app module)

The last and probably the most used one is build.gradle inside our app module. It is used for defining the configurations and settings used for this particular module. Functionality-wise, it has similar options as project-level build.gradle, but it brings a separation of concerns. So all the libraries and frameworks, that we want to use in our Kotlin code that is placed inside our app module, should be defined in dependencies block from this gradle file. All the plugins that are required for these libraries to work should also be defined in the same gradle file.

If we take a look inside our app module build.gradle, we can find three main sections:

  1. Plugins,
  2. Android,
  3. Dependencies.

We are already familiar with the plugins and dependencies sections (they are quite similar to the ones we already covered); the new thing here is the android section.

This is a place where we can do a lot of configuration changes to our app. The most common things developers set here are:

  1. The versions of minSdk, targetSdk and compileSdk.
  2. Version code and name of the app.
  3. Product flavors and build types.

Here is an example of android section of module-level build.gradle file.

build.gradle (app modle)

In this case, we see a lot of keywords and values. So you may think, by looking at Groovy implementation, that these are all functions that expect one parameter. Well… now, we come to the point that I found a bit confusing. Actually, on the left side these are all the attributes, and on the right side, those are their respected values. So assigning the values to the attributes is done without an equal sign. That’s why I wanted to show you Kotlin equivalent, because I believe you are able to understand it better since we are coming from Java/Kotlin background.

Now, we have covered most of the basic things that are mentioned in our gradle files, so I would like to conclude this tour of gradle files inside our Android projects.

Final thoughts

As you saw, gradle files are built from the lines of code. Like any other programming language, there are variables and there are functions. Gradle files are no exception.

Hopefully, you gained a bit of knowledge regarding its purpose and gained some insights of how you can read what is happening inside those files.

If you found this article helpful, please follow for more similar content. If you prefer a bite-sized content about Android, you can consider following me on Twitter. In case you want to connect on a more professional level, here is my LinkedIn.

Thanks for your time!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ban Markovic

Android Engineer who writes about his learnings of Jetpack Compose