Advanced Gradle for Android Tips

There are lots of things you can do given a full scripting language
as the basis for your build system. This chapter represents a collection
of tips for things that you can do that go beyond stock capabilities
provided by the Android Gradle Plugin.

Prerequisites

Understanding this chapter requires that you have read the chapters that
introduce Gradle and cover
basic Gradle/Android integration, including
the project structure.

Gradle, DRY

Ideally, your build scripts do not repeat themselves any more than is
logically necessary. For example, a project and sub-projects probably
should use the same version of the build tools, yet by default, we
define them in each build.gradle file. This section outlines some
ways to consolidate this sort of configuration.

It’s build.gradle All The Way Down

If you have sub-projects, you can have build.gradle files at
each level of your project hierarchy. Your top-level build.gradle
file is also applied to the sub-projects when they are built.

Sub-projects can then reference rootProject.ext to retrieve
those values:

android{compileSdkVersionrootProject.ext.compileSdkVersion}

By this means, you can ensure that whatever needs to be synchronized
at build time is synchronized, by defining it once.

Another way that a top-level build.gradle file can configure
subprojects is via the subprojects closure. This contains
bits of configuration that will be applied to each of the
subprojects as a part of their builds.

Note that subprojects applies to all sub-projects (a.k.a., modules), which limits its
utility. For example, a top-level project with one sub-project for an app
and another sub-project for a library used by that app cannot readily use
subprojects. That is because the library sub-project needs to configure
the com.android.library plugin, while the application sub-project needs to
configure the com.android.application plugin. The subprojects closure is only good for
common configuration to apply to all sub-projects regardless of project type.

gradle.properties

Another approach would be to add a gradle.properties file
to your project root directory. Those properties are automatically read
in and would be available up and down your project hierarchy.

Per-developer properties can go in a gradle.properties file in the
user’s Gradle home directory (e.g., ~/.gradle on Linux), where they
will not be accidentally checked into version control.

So, to achieve the synchronized compileSdkVersion value, you could
have a gradle.properties file with:

COMPILE_SDK_VERSION=26

Then, your projects’ build.gradle files could use:

android{compileSdkVersionCOMPILE_SDK_VERSION}

Custom Properties Files

You are also welcome to use your own custom properties files. For example,
perhaps you want to use gradle.properties for properties that you are willing
to put in version control (e.g., BUILD_TOOLS_VERSION), but you would also
like to use a properties file to keep your code-signing details outside of
your build.gradle file and out of version control.

Loading in custom properties files is slightly clunky, as it does not appear
to be built into Gradle itself. However, you can take advantage of the fact
that Gradle is backed by Groovy and use some ordinary Groovy code to load
the properties.

These three lines create a java.util.Properties object from a keystore.properties
file located in the module’s directory. Individual properties can be read
using <> syntax, the same as Groovy uses for a Map:

Now, your signing information is not in build.gradle, and you can keep
a fake keystore.properties in version control (where the properties have
invalid values), enough to allow Gradle to process the build.gradle file
but not allow for app signing. The machine designated for doing official
builds would have the real keystore.properties file and associated keystore.

Environment Variables

Any environment variables with a prefix of ORG_GRADLE_PROJECT_ will show up
as global variables in your Gradle script. So, for example, you can access
an environment variable named ORG_GRADLE_PROJECT_foo by accessing a foo
variable in build.gradle.

If you would prefer to use environment variables without that prefix,
you can call System.getenv(), passing in the name of the environment variable,
to retrieve its value.

Note, however, that you may or may not have access to the environment variables
that you think you should. Android Studio, for example, does not expose
environment variables to Gradle for its builds, and so an environment variable
that you can access perfectly well from the command line may not be available
in the same build.gradle script when run from Android Studio.