5 Basics of Gradle for Android Developers

Dmitrii Leonov
4 min readAug 1, 2022

--

Does Gradle script look like a magic spell to you? You’ve been writing apps for some time and your interactions with Gradle were limited to adding dependencies, updating compile and target SDK, Gradle plugin version, working with build flavors and build types, and going to Stack Overflow every time your build fails. Maybe you even went as far as splitting the monolith app into modules and touched gradle.settings but still haven’t read a page of Gradle docs? Then this article is for you!

What is Gradle?

Gradle is a build automation tool that uses build script and through configuration describes information about the application. Gradle then uses that information to build your app. Gradle build scripts are written using Groovy or Kotlin DSL.

In this article, I am focusing on Groovy DSL due to it being widely used. In addition to that, in order to migrate an existing project into Kotlin DSL, a developer should have at least some understanding of what’s going on in Groovy script.

What makes Gradle so confusing for many of us?

  • Not paying attention to it as it is just a build tool and not the product it is building.
  • Many ways of doing the same thing + Legacy support.
  • Stack Overflow with even more legacy code snippets.
  • Groovy magic.

Let’s try to understand Gradle a little bit.

1) Why do we use gradlew taskName instead of gradle taskName to execute a task?

It is because in our projects we use Gradle wrapper to have 1 version of Gradle among all the developers on a project. In fact, you probably don’t have Gradle installed on your machine so you can't even execute gradle commands outside of projects with the Gradle wrapper.

2) Android Studio’s Modules and Gradle’s Projects

An Android project is also a Gradle project. Gradle has a 1-to-1 relationship between its projects and build.gralde files. At the same time what Android Studio calls a "module", in Gradle is referred to as a project.

3) Should I use ' or " for string literals?

Groovy recognizes both double and single quotation marks as a string.
Not all Strings are made equal. String types in Groovy:

  • ‘Hello World 1’ — String
  • “Hello World 2” — String
  • “Hello World $count” — GString

4) Function calls, properties, and when to use round brackets?
In Groovy:

  • If a function has at least one parameter curly brackets can be ignored.
  • Fields are properties, so each class field generates a setter and getter for a declared field.

Look at the code below and try to guess, how many times the Foo.name() function will be triggered when executing ./gradlew showMagic (Mac) or gradlew.bat showMagic (Windows).

Output:

showMagic() string 1
Foo.name() triggered
showMagic() string 2
Foo.name() triggered
showMagic() string 3
showMagic() string 4
showMagic() string 5
showMagic() string 6

As you might have already guessed from the point above, we can ignore round brackets and pass multiple parameters separated by commas:

Knowing this, you can decide what style to stick to and keep it consistent across all Gradle files in the project.

5) Not knowing the main Gradle entities and building blocks

When you don’t know the terms and names of what you are using, it gets harder to look for a solution. There are building blocks and entities that are common for every Gradle project (do not mistake with ones added by the Android plugin).

Entities:

  • Project — a top-level entity that allows you to interact with all other entities from the build file. It is a so-called “God object”. Gradle documentation refers to it as a “collection of Gradle tasks”.
  • Task — has a number of default properties, which you've probably seen such as group,description ,dependsOn and methods doFirst , doLast and a mix of those two that you can find in docs. An important thing to know about the tasks is that they have outcomes/states, which determine how fast the task execution will be.
  • Action — the actual code that gets executed during the task run.
  • Plugin — a set of tasks and configurations that extend the functionality of Gradle plugin. Added with apply plugin: <pluginName>, apply from: <file.gradle>

Build script blocks:

  • buildScript {} — this is where it all starts. You can define additional dependencies to later apply plugins and extend functionality. It usually contains repositories {} and dependencies {} .
  • repositories {}— declare sources where dependencies and plugins are going to be fetched from.
  • configurations {} — you probably haven’t seen this one, but this is a block to declare scopes for the dependencies to be used inside the dependeincies{} block. You can think of it as a block where you configure scopes for example with logic for dependency conflict resolution strategy. Examples of configurations are classPath, implementation, api and ones that the Android plugin adds androidTestImplementation, debugImplementation, etc.
  • dependencies {} — declare dependencies/libraries that are used in a project.
  • plugins {} — it must be the first block of the script. It contains applied plugin ids id 'plugin.name'. This block is of type PluginDependenciesSpec and can be replaced with apply plugin: <pluginName> instead of plugin id and apply from: 'dependencies.gradle' to apply dependency script.
  • allProjects {} / subProjects {}— what are the repositories and dependencies that need to be applied to every project/sub-project.

I hope now, after reading this article, Gradle scripts are looking less like a magic spell and making more sense to you!

Next article: Understanding Gradle Tasks

Connect with me:
- Twitter: https://twitter.com/leonov_dmitrii
- LinkedIn: https://www.linkedin.com/in/dmitrii-leonov/

--

--

Dmitrii Leonov

Android Developer | Getting better at my craft by sharing knowledge!