Understanding Gradle basics
In order for an Android project to be built using Gradle, you need to set up a build script. This will always be called build.gradle
, by convention. You will notice, as we go through the basics, that Gradle favors convention over configuration and generally provides default values for settings and properties. This makes it a lot easier to get started with a lot less configuration than that found in systems such as Ant or Maven, which have been the de facto build systems for Android projects for a long time. You do not need to absolutely comply with these conventions though, as it is usually possible to override them if needed.
Gradle build scripts are not written in the traditional XML, but in a domain-specific language (DSL) based on Groovy, a dynamic language for the Java Virtual Machine (JVM). The team behind Gradle believes that using a declarative, DSL-style approach based on a dynamic language has significant advantages over using the more procedural, free-floating style of Ant, or any XML-based approach used by many other build systems.
That does not mean you need to know Groovy to get started with your build scripts. It is easy to read, and if you already know Java, the learning curve is not that steep. If you want to start creating your own tasks and plugins (which we will talk about in later chapters), it is useful to have a deeper understanding of Groovy. However, because it is based on the JVM, it is possible to write code for your custom plugins in Java or any other JVM-based language.
Projects and tasks
The two most important concepts in Gradle are projects and tasks. Every build is made up of at least one project, and every project contains one or more tasks. Every build.gradle
file represents a project. Tasks are then simply defined inside the build script. When initializing the build process, Gradle assembles Project
and Task
objects based on the build file. A Task
object consists of a list of Action
objects, in the order they need to be executed. An Action
object is a block of code that is executed, similar to a method in Java.
The build lifecycle
Executing a Gradle build is, in its simplest form, just executing actions on tasks, which are dependent on other tasks. To simplify the build process, the build tools create a dynamic model of the workflow as a Directed Acyclic Graph (DAG). This means all the tasks are processed one after the other and loops are not possible. Once a task has been executed, it will not be called again. Tasks without dependencies will always be run before the others. The dependency graph is generated during the configuration phase of a build. A Gradle build has three phases:
- Initialization: This is where the
Project
instance is created. If there are multiple modules, each with their ownbuild.gradle
file, multiple projects will be created. - Configuration: In this phase, the build scripts are executed, creating and configuring all the tasks for every project object.
- Execution: This is the phase where Gradle determines which tasks should be executed. Which tasks should be executed depends on the arguments passed for starting the build and what the current directory is.
The build configuration file
In order to have Gradle build a project, there always needs to be a build.gradle
file. A build file for Android has a few required elements:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' } }
This is where the actual build is configured. In the repositories block, the JCenter repository is configured as a source of dependencies for the build script. JCenter is a preconfigured Maven repository and requires no extra setup; Gradle has you covered. There are several repositories available straight from Gradle and it is easy to add your own, either local or remote.
The build script block also defines a dependency on Android build tools as a classpath Maven artifact. This is where the Android plugin comes from. The Android plugin provides everything needed to build and test applications. Every Android project needs to apply the Android plugin using this line:
apply plugin: 'com.android.application'
Plugins are used to extend the capabilities of a Gradle build script. Applying a plugin to a project makes it possible for the build script to define properties and use tasks that are defined in the plugin.
Note
If you are building a library, you need to apply 'com.android.library'
instead. You cannot use both in the same module because that would result in a build error. A module can be either an Android application or an Android library, not both.
When using the Android plugin, Android-specific conventions can be configured and tasks only applicable to Android will be generated. The Android block in the following snippet is defined by the plugin and can be configured per project:
android { compileSdkVersion 22 buildToolsVersion "22.0.1" }
This is where the Android-specific part of the build is configured. The Android plugin provides a DSL tailored to Android's needs. The only required properties are the compilation target and the build tools. The compilation target, specified by compileSdkVersion
, is the SDK version that should be used to compile the app. It is good practice to use the latest Android API version as the compilation target.
There are plenty of customizable properties in the build.gradle
file. We will discuss the most important properties in Chapter 2, Basic Build Customization, and more possibilities throughout the rest of the book.
The project structure
Compared to the old Eclipse projects, the folder structure for Android projects has changed considerably. As mentioned earlier, Gradle favors convention over configuration and this also applies to the folder structure.
This is the folder structure that Gradle expects for a simple app:
MyApp ├── build.gradle ├── settings.gradle └── app ├── build.gradle ├── build ├── libs └── src └── main ├── java │ └── com.package.myapp └── res ├── drawable ├── layout └── etc.
Gradle projects usually have an extra level at the root. This makes it easier to add extra modules at a later point. All source code for the app goes into the app
folder. The folder is also the name of the module by default and does not need to be named app. If you use Android Studio to create a project with both a mobile app and an Android Wear smartwatch app, for example, the modules are called application and wearable by default.
Gradle makes use of a concept called source set. The official Gradle documentation explains that a source set is a group of source files, which are compiled and executed together. For an Android project, main
is the source set that contains all the source code and resources for the default version of the app. When you start writing tests for your Android app, you will put the source code for the tests inside a separate source set called androidTest
, which only contains tests.
Here is a short overview of the most important folders of an Android app:
Directory |
Content |
---|---|
|
The source code for the app |
|
These are app-related resources (drawables, layouts, strings, and so on) |
|
These are external libraries ( |
|
The output of the build process |