The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


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.

In particular, you can “pass data” from the top-level build.gradle file to sub-projects by configuring the ext object via a closure. In the top-level build.gradle file, you would put common values to be used:

ext {
   compileSdkVersion=26
}

(note the use of the = sign here)

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

android {
    compileSdkVersion rootProject.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 {
    compileSdkVersion COMPILE_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.

def keystorePropertiesFile = file('keystore.properties')
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

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:

signingConfigs {
  release {
    keyAlias keystoreProperties['keyAlias']
    keyPassword keystoreProperties['keyPassword']
    storeFile file(keystoreProperties['storeFile'])
    storePassword keystoreProperties['storePassword']
  }
}

This fills in the signing configuration from the keystore.properties values, assuming that the keystore.properties file itself has the appropriate properties:

storePassword=81016168
keyPassword=9f353470
keyAlias=foo
storeFile=release.jks

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.

Automating APK Version Information

The preview of this section was eaten by a grue.

Adding to BuildConfig

The preview of this section is in an invisible, microscopic font.