Adding a pre-build step to Android Studio builds

January 20, 2020  2 minute read  

I’m currently writing a cloud-native app with an Android (and iOS) front end. I’ve got my backend configuration written using Terraform, and it outputs a file called infrastructure.json that describes the backend in a JSON format. Now, the question becomes “how do I get that infrastructure.json file into my front end code?”

A simple version of this would be to ensure I copy the file every single time I update it. However, a better solution would be to ensure the file exists and to copy it to the right place. An even better solution would be to pull the information I need and write out the correct JSON format for Android Studio.

In this example, I’m going to read in a JSON file from outside the Android Studio area, and write a JSON file to res/raw.

Gradle is Groovy

Literally. The language underlying gradle, which is used for builds by Android Studio, is Groovy. This is good for us, because it means we have a lot of language features at our disposal. I’m going to use the file management and JSON serialization features in this example.

Start by creating a file called prebuild.gradle at the same level as the project build.gradle. Fill it with these contents:

import groovy.json.JsonSlurper
import groovy.json.JsonOutput

task generateAppConfigurationFile(type:Exec) {
    def jsonSlurper = new JsonSlurper()
    def data = jsonSlurper.parse(new File('../../infrastructure.json'))

    // If necessary, construct a new JSON object, then continue
    // Change data here to the new object
    def json_str = JsonOutput.toJson(data)

    // Write the json_str to the configuration file
    def config_file = new File('app/src/main/res/raw/config.json')

build.dependsOn generateAppConfigurationFile

This creates a task called generateAppconfigurationFile. It reads in the JSON file specified (relative to this gradle file). Once read in, you could create a new object. For instance:

def newData = [:]
newData.baseUrl = data.base_url.value
newData.clientId = data.client_id.value

Once I’ve got the right data, I create the JSON string, and finally write it to the configuration file (again, relative to this gradle file).

The final line is the important one - it hooks this task into the build process, ensuring the build phase depends on this task, thus ensuring that the configuration file will exist before building starts.

If infrastructure.json or the app/src/main/res/raw directory doesn’t exist, then the task will fail, and hence the whole build will fail. I’ve placed a .gitignore file in the app/src/main/res/raw directory to explicitly prevent config.json from being checked in. This will also ensure that the directory exists when you clone the repository.

Hooking the prebuild step into the configuration

Now, go to the module-level build.gradle file. For me, this is app/build.gradle. Add the following line to the top:

apply from: '../prebuild.gradle'

Mine is right under the apply plugin: lines.

Build away!

Whenever you build your app, the configuration file will be re-generated for you!



Leave a comment