Introducing Gradle and the Manifest

In the discussion of Android Studio, this book has mentioned something called “Gradle”, without a lot of explanation.

In this chapter, the mysteries of Gradle will be revealed to you.

(well, OK, some of the mysteries…)

We also mentioned in passing in the previous chapter the concept of the “manifest”, as being a special file in our Android projects.

On the one hand, Gradle and the manifest are not strictly related. On the other hand, some (but far from all) of the things that we can set up in the manifest can be overridden in Gradle.

So, in this chapter, we will review both what Gradle is, what the manifest is, what each of their roles are, and the basics of how they tie together.

Gradle: The Big Questions

First, let us “set the stage” by examining what this is all about, through a series of fictionally-asked questions (FAQs).

What is Gradle?

Gradle is software for building software, otherwise known as “build automation software” or “build systems”. You may have used other build systems before in other environments, such as make (C/C++), rake (Ruby), Ant (Java), Maven (Java), etc.

These tools know — via intrinsic capabilities and rules that you teach them — how to determine what needs to be created (e.g., based on file changes) and how to create them. A build system does not compile, link, package, etc. applications directly, but instead directs separate compilers, linkers, and packagers to do that work.

Gradle uses a domain-specific language (DSL) built on top of Groovy to accomplish these tasks.

What is Groovy?

There are many programming languages that are designed to run on top of the Java VM. Some of these, like JRuby and Jython, are implementations of other common programming languages (Ruby and Python, respectively). Other languages are unique, and Groovy is one of those.

Groovy scripts look a bit like a mashup of Java and Ruby. As with Java, Groovy supports:

As with Ruby, though:

Groovy is an interpreted language, like Ruby and unlike Java. Groovy scripts are run by executing a groovy command, passing it the script to run. The Groovy runtime, though, is a Java JAR and requires a JVM in order to operate.

One of Groovy’s strengths is in creating a domain-specific language (or DSL). Gradle, for example, is a Groovy DSL for doing software builds. Gradle-specific capabilities appear to be first-class language constructs, generally indistinguishable from capabilities intrinsic to Groovy. Yet, the Groovy DSL is largely declarative, like an XML file.

To some extent, we get the best of both worlds: XML-style definitions (generally with less punctuation), yet with the ability to “reach into Groovy” and do custom scripting as needed.

What Does Android Have To Do with Gradle?

Google has published the Android Plugin for Gradle, which gives Gradle the ability to build Android projects. Google is also using Gradle and Gradle for Android as the build system behind Android Studio.

Why Did We Move to Gradle?

Originally, when we would build an app, those builds were done using Eclipse and Ant. Eclipse was the IDE, while Ant was the command-line tool. Eclipse does not use Ant for building Android projects, but rather has its own build system. And we were successfully building a million-plus apps using these tools. Those tools still work today, though Ant support is fading fast.

So, why change?

There were several contributing factors, including:

How Does Gradle Relate to Android Studio?

As noted above, Android Studio uses the new Gradle-based build system as its native approach for building Android projects. While the IntelliJ IDEA IDE that serves as Android Studio’s core also has its own build system (much like Eclipse has one), IDEA is more amenable to replaceable build systems.

Over time, this will allow Google to focus on a single build system (Gradle) for all supported scenarios, rather than having to deal with a collection of independent build systems.

Obtaining Gradle

As with any build system, to use it, you need the build system’s engine itself.

If you will only be using Gradle in the context of Android Studio, the IDE will take care of getting Gradle for you. If, however, you are planning on using Gradle outside of Android Studio (e.g., command-line builds), you will want to consider where your Gradle is coming from. This is particularly important for situations where you want to build the app with no IDE in sight, such as using a continuous integration (CI) server, like Jenkins.

Direct Installation

What most developers looking to use Gradle outside of Android Studio will wind up doing is installing Gradle directly.

The Gradle download page contains links to ZIP archives for Gradle itself: binaries, source code, or both.

You can unZIP this archive to your desired location on your development machine.

Linux Packages

You may be able to obtain Gradle via a package manager on Linux environments. For example, there is an Ubuntu PPA for Gradle.

The gradlew Wrapper

If you are starting from a project that somebody else has published, you may find a gradlew and gradlew.bat file in the project root, along with a gradle/ directory. This represents the “Gradle Wrapper”.

The Gradle Wrapper consists of three pieces:

Android Studio uses the gradle-wrapper.properties file to determine where to download Gradle from, for use in your project, from the distributionUrl property in that file:

#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

(from Basic/Button/gradle/wrapper/gradle-wrapper.properties)

When you create or import a project, or if you change the version of Gradle referenced in the properties file, Android Studio will download the Gradle pointed to by the distributionUrl property and install it to a .gradle/ directory (note the leading .) in your project. That version of Gradle will be what Android Studio uses.

RULE #1: Only use a distributionUrl that you trust.

If you are importing an Android project from a third party — such as the samples for this book — and they contain the gradle/wrapper/gradle-wrapper.properties file, examine it to see where the distributionUrl is pointing to. If it is loading from services.gradle.org, or from an internal enterprise server, it is probably trustworthy. If it is pointing to a URL located somewhere else, consider whether you really want to use that version of Gradle, considering that it may have been tampered with.

The batch file, shell script, and JAR file are there to support command-line builds. If you use gradlew, it will use a local copy of Gradle installed in .gradle/ in the project. If there is no such copy of Gradle, gradlew will download Gradle from the distributionUrl, as does Android Studio. Note that Android Studio does not use gradlew for this role — that logic is built into Android Studio itself.

RULE #2: Only use a gradlew that you REALLY trust.

It is relatively easy to examine a .properties file to check a URL to see if it seems valid. Making sense of a batch file or shell script can be cumbersome. Decompiling a JAR file and making sense of it can be rather difficult. Yet, if you use gradlew that you obtained from somebody, that script and JAR are running on your development machine, as is the copy of Gradle that they install. If that code was tampered with, the malware has complete access to your development machine and anything that it can reach, such as servers within your organization.

Note that you do not have to use the Gradle Wrapper at all. If you would rather not worry about it, install a version of Gradle on your development machine yourself and remove the Gradle Wrapper files. You can use the gradle command to build your app (if your Gradle’s bin/ directory is in your PATH), and Android Studio will use your Gradle installation (if you teach it where to find it, such as via the GRADLE_HOME environment variable).

Versions of Gradle and the Android Plugin for Gradle

The Android Plugin for Gradle that we will use to give Gradle “super Android powers!” is updated periodically. Each update has its corresponding required version of Gradle.

The rules, according to Google, are:

Android Plugin for Gradle Versions Supported Gradle Versions
1.0.0 - 1.1.3 2.2.1 - 2.3
1.2.0 - 1.3.1 2.2.1 - 2.9
1.5.x 2.2.1 or higher
2.x 2.10 or higher

If you are using the Gradle Wrapper, you are using an installation of Gradle that is local to the project. So long as the version of Gradle in the project matches the version of Gradle for Android requested in the build.gradle file — as will be covered in the next chapter — you should be in fine shape.

If you are not using the Gradle Wrapper, you will need to decide when to take on a new Gradle for Android release and plan to update your Gradle installation and build.gradle files in tandem at that point.

Gradle Environment Variables

If you installed Gradle yourself, you will want to define a GRADLE_HOME environment variable, pointing to where you installed Gradle, and to add the bin/ directory inside of Gradle to your PATH environment variable.

You may also consider setting up a GRADLE_USER_HOME environment variable, pointing to a directory in which Gradle can create a .gradle subdirectory, for per-user caches and related materials. By default, Gradle will use your standard home directory.

Examining the Gradle Files

An Android Studio project usually has two build.gradle files, one at the project level and one at the “module” level (e.g., in the app/ directory).

The Project-Level File

The build.gradle file in the project directory controls the Gradle configuration for all modules in your project. Right now, most likely you only have one module, and many apps only ever use one module. However, it is possible for you to add other modules to this project, and we will explore reasons for doing so later in this book.

Here is a typical top-level build.gradle file:


// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

buildscript

The buildscript closure (i.e., code section wrapped in braces) in Gradle is where you configure the JARs and such that Gradle itself will use for interpreting the rest of the file. Hence, here you are not configuring your project so much as you are configuring the build itself.

The repositories closure inside the buildscript closure indicates where dependencies can come from, typically in the form of Maven-style repositories. Here, jcenter() is a built-in method that sets up the repository information for Maven Central, a popular location for obtaining open source dependencies.

The dependencies closure indicates what is required to be able to run the rest of the build script. classpath 'com.android.tools.build:gradle:2.2.2' is not especially well-documented by the Gradle team. However the 'com.android.tools.build:gradle:2.2.2' portion means:

The first time you run your build, with the buildscript closure as shown above, Gradle will notice that you do not have this dependency. It will then download that artifact from the jcenter() repository.

Sometimes, the last segment of the version is replaced with a + sign (e.g., 2.2.+). This tells Gradle to download the latest version, thereby automatically upgrading you to the latest patch-level (e.g., 2.2.3 at some point).

allprojects

The allprojects closure says “apply these settings to all modules in this project”. Here, we are setting up jcenter() as a repository to use for finding libraries used in any of the modules in our project.

The Module-Level Gradle File

In your app/ module, you will also find a build.gradle file. This has settings unique for this module, independent of any other module that your project may have in the future.

Here is a typical module-level build.gradle file:

apply plugin: 'com.android.application'

dependencies {

}

android {
    compileSdkVersion 19
    buildToolsVersion "21.1.2"
}

(from Basic/Button/app/build.gradle)

dependencies

This build.gradle file also has a dependencies closure. Whereas the dependencies closure in the buildscript closure in the top-level build.gradle file is for libraries used by the build process, the dependencies closure in the module’s build.gradle file is for libraries used by your code in that module. We will get into the concept of these libraries later in the book.

This particular build.gradle file has an empty dependencies closure, meaning that it does not depend on any libraries. The dependencies closure is not required in this case — it is here solely for illustration purposes.

android

The android closure contains all of the Android-specific configuration information. This closure is what the Android plugin enables.

But before we get into what is in this closure, we should “switch gears” and talk about the manifest file, as what goes in the android closure is related to what goes in the manifest file.

Introducing the Manifest

The foundation for any Android application is the manifest file: AndroidManifest.xml. This will be in your app module’s src/main/ directory for classic Android Studio projects.

Here is where you declare what is inside your application — the activities, the services, and so on. You also indicate how these pieces attach themselves to the overall Android system; for example, you indicate which activity (or activities) should appear on the device’s main menu (a.k.a., launcher).

When you create your application, you will get a starter manifest generated for you. For a simple application, offering a single activity and nothing else, the auto-generated manifest will probably work out fine, or perhaps require a few minor modifications. On the other end of the spectrum, the manifest file for the Android API demo suite is over 1,000 lines long. Your production Android applications will probably fall somewhere in the middle.

As mentioned previously, some items can be defined in both the manifest and in a build.gradle file. The approach of putting that stuff in the manifest still works. For Android Studio users, you will probably use the Gradle file and not have those common elements be defined in the manifest.

Things In Common Between the Manifest and Gradle

There are a few key items that can be defined in the manifest and can be overridden in build.gradle statements. These items are fairly important to the development and operation of our Android apps as well.

Package Name and Application ID

The root of all manifest files is, not surprisingly, a manifest element:


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.commonsware.empublite">

Note the android namespace declaration. You will only use the namespace on many of the attributes, not the elements (e.g., <manifest>, not <android:manifest>).

The biggest piece of information you need to supply on the <manifest> element is the package attribute.

The package attribute will always need to be in the manifest, even for Android Studio projects. The package attribute will control where some source code is generated for us, notably some R and BuildConfig classes that we will encounter later in the book.

Since the package value is used for Java code generation, it has to be a valid Java package name. Java convention says that the package name should be based on a reverse domain name (e.g., com.commonsware.empublite), where you own the domain in question. That way, it is unlikely that anyone else will accidentally collide with the same name.

The package also serves as our app’s default “application ID”. This needs to be a unique identifier, such that:

By default, the application ID is the package value, but Android Studio users can override it in their Gradle build files. Specifically, inside of the android closure can be a defaultConfig closure, and inside of there can be an applicationId statement:


android {
    // other stuff

    defaultConfig {
        applicationId "com.commonsware.empublite"
        // more other stuff
    }
}

Not only can Android Studio users override the application ID in the defaultConfig closure, but there are ways of having different application ID values for different scenarios, such as a debug build versus a release build. We will explore that more later in the book.

minSdkVersion and targetSdkVersion

Your manifest may also contain a <uses-sdk> element as a child of the <manifest> element, to specify what versions of Android you are supporting. It can contain, among other things, android:minSdkVersion and android:targetSdkVersion attributes. Legacy Eclipse-style projects will always have this element. Android Studio projects may not have this element, because the values are defined as minSdkVersion and targetSdkVersion properties in the defaultConfig closure, where applicationId can be defined.

Of the two, the more critical one is minSdkVersion. This indicates what is the oldest version of Android you are testing with your application. The value of the attribute is an integer representing the Android API level. So, if you are only testing your application on Android 4.1 and newer versions of Android, you would set your minSdkVersion to be 16.

You can also specify a targetSdkVersion. This indicates what version of Android you are thinking of as you are writing your code. If your application is run on a newer version of Android, Android may do some things to try to improve compatibility of your code with respect to changes made in the newer Android. Nowadays, most Android developers should specify a target SDK version of 15 or higher. We will start to explore more about the targetSdkVersion as we get deeper into the book; for the moment, whatever your IDE gives you as a default value is probably a fine starting point.

The XML element looks like:


<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="19" />

The corresponding entries in build.gradle go in the defaultConfig closure:


android {
    // other stuff

    defaultConfig {
        applicationId "com.commonsware.empublite"
        minSdkVersion 15
        targetSdkVersion 19
        // more other stuff
    }
}

Version Code and Version Name

Your manifest can also specify android:versionName and android:versionCode attributes, up on the root <manifest> element. An Android Studio project, though, frequently skips those and defines them via versionName and versionCode properties in the defaultConfig closure.

These two values represent the versions of your application. The versionName value is what the user will see for a version indicator in the Applications details screen for your app in their Settings application:

Barcode Scanner App Screen in Settings, Showing Version 4.2
Figure 53: Barcode Scanner App Screen in Settings, Showing Version 4.2

Also, the version name is used by the Play Store listing, if you are distributing your application that way. The version name can be any string value you want.

The versionCode, on the other hand, must be an integer, and newer versions must have higher version codes than do older versions. Android and the Play Store will compare the version code of a new APK to the version code of an installed application to determine if the new APK is indeed an update. The typical approach is to start the version code at 1 and increment it with each production release of your application, though you can choose another convention if you wish. During development, you can leave these alone, but when you move to production, these attributes will matter greatly.

Other Gradle Items of Note

You will always have at least two statements directly in the android closure: compileSdkVersion and buildToolsVersion.

android {
    compileSdkVersion 19
    buildToolsVersion "21.1.2"
}

(from Gradle/HelloNew/build.gradle)

compileSdkVersion specifies the API level to be compiled against, usually as a simple API level integer (e.g., 19). A legacy Eclipse-style project would pull this out of the project.properties file in the root of the project directory.

buildToolsVersion indicates the version of the Android SDK build tools that you wish to use with this project. While downloading the android plugin from Maven Central gives us parts of what is needed, it is not complete. The rest comes from the “Android SDK Build-tools” entry in the SDK Manager:

SDK Manager, Showing Android SDK Build-tools
Figure 54: SDK Manager, Showing “Android SDK Build-tools”

Note that the SDK Manager will allow you to download the latest version of the build tools used by Gradle (appearing as 20 in the above screenshot) plus prior versions (e.g., 18.0.1 and 17 in the above screenshot). This corresponds with the buildToolsVersion in your build.gradle file.

So, your android closure could look like:


android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.commonsware.empublite"
        versionCode 1
        versionName "1.0"
        minSdkVersion 15
        targetSdkVersion 18
    }
}

Eclipse did not really have the notion of a configurable build tools version, so there is no analogue for buildToolsVersion in a legacy Eclipse-style project.

Where’s the GUI?

You might wonder why we have to slog through all of this Groovy code and wonder if there is some GUI for affecting Gradle settings.

The answer is yes… and no.

There is the project structure dialog, that allows you to maintain some of this stuff. And you are welcome to try it. However, the more complex your build becomes, the more likely it is that the GUI will not suffice, and you will need to work with the Gradle build files more directly. Hence, this book will tend to focus on the build files.

The Rest of the Manifest

Not everything in the manifest can be overridden in the Gradle build files. Here are a few key items that will always appear in the manifest, regardless of whether this project is to be built by Android Studio or other means.

An Application For Your Application

In your initial project’s manifest, the primary child of the <manifest> element is an <application> element.

By default, when you create a new Android project, you get a single <activity> element inside the <application> element:

<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.commonsware.android.skeleton"
  android:versionCode="1"
  android:versionName="1.0">

  <application>
    <activity
      android:name="Now"
      android:label="Now">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
  </application>

</manifest>
(from Skeleton/Now/AndroidManifest.xml)

This element supplies android:name for the class implementing the activity, android:label for the display name of the activity, and (sometimes) an <intent-filter> child element describing under what conditions this activity will be displayed. The stock <activity> element sets up your activity to appear in the launcher, so users can choose to run it. As we’ll see later in this book, you can have several activities in one project, if you so choose.

The android:name attribute, in this case, has a bare Java class name (Now). Sometimes, you will see android:name with a fully-qualified class name (e.g., com.commonsware.android.skeleton.Now). Sometimes, you will see a Java class name with a single dot as a prefix (e.g., .Now). Both Now and .Now refer to a Java class that will be in your project’s package — the one you declared in the package attribute of the <manifest> element.

Supporting Multiple Screens

Android devices come with a wide range of screen sizes, from 2.8” tiny smartphones to 46” TVs. Android divides these into four buckets, based on physical size and the distance at which they are usually viewed:

  1. Small (under 3”)
  2. Normal (3” to around 4.5”)
  3. Large (4.5” to around 10”)
  4. Extra-large (over 10”)

By default, your application will support small and normal screens. It also will support large and extra-large screens via some automated conversion code built into Android.

To truly support all the screen sizes you want, you should consider adding a <supports-screens> element to your manifest. This enumerates the screen sizes you have explicit support for. For example, if you are providing custom UI support for large or extra-large screens, you will want to have the <supports-screens> element. So, while the starting manifest file works, handling multiple screen sizes is something you will want to think about.

You wind up with an element akin to:


<supports-screens
  android:largeScreens="true"
  android:normalScreens="true"
  android:smallScreens="false"
  android:xlargeScreens="true" />

Much more information about providing solid support for all screen sizes, including samples of the <supports-screens> element, will be found later in this book as we cover large-screen strategies.

Other Stuff

As we proceed through the book, you will find other elements being added to the manifest, such as:

These and other elements will be introduced elsewhere in the book.

Learning More About Gradle

This book will go into more about Gradle, both in the core chapters and in the trails. But, the focus will be on Gradle for Android, and Gradle itself offers a lot more than that. The Gradle Web site hosts documentation, links to Gradle-specific books, and links to other Gradle educational resources.

At present, the Gradle for Android documentation is limited and mostly appears on the Android tools site. Of note is the top-level page about the new build system, and the Gradle plugin user guide, though both may be out of date compared to the actual tools themselves.

Visit the Trails!

There are a few more chapters in this book getting into more details about the use of Gradle and Gradle for Android.

There is also the “Advanced Gradle for Android Tips” chapter for other Gradle topics, and the chapter on manifest merging in Gradle.