TIL: Gradle tasks and doLast

I’ve known about custom Gradle tasks and functions for a while (Android Studio). I’ve used one, copied from SO and edited to suit my needs, in one of my projects before. This week I got to create a task from scratch to add a dev feature to the app at work. So far so good.

This TIL is not about learning about Gradle tasks. This is about a tiny detail that I didn’t know. I usually write/see tasks written like this

task countChicken {
    // count chicken
    ...
}

When written like this, the chicken are counted immediately every time the Gradle project is synced or built (task configuration).

What I had not known till this week was that anything directly inside that task definition block is executed at the task configuration stage. So, in this case the whole counting of chicken is happening at the task configuration stage, whether we explicitly execute the task or not.

If we only want to execute the task on demand, the task configuration needs to be separated from execution, like so:

task countChicken {
    doLast {
        // count chicken
        ...
    }
}

The doLast ensures that this code block is only executed during task execution. It basically tells Gradle during configuration to run this code block last while executing this task1. We can also use the doFirst code block to run something first while executing the task.


Why did I only discover this? The Gradle task in my personal project is set up such that I want it to run every time for all builds, and running it multiple times has no impact. So, I never considered when it was being executed, as long as it was executed at least once per build.

In the current use case, I wanted the task to only be executed for a single build type. So, I was using some logic like below:

afterEvaluate {
    android.applicationVariants.all {  variant ->
        if ( variant.buildType.name == "debug") {
            variant.javaCompiler.dependsOn(countChicken)
        }
    }
}

But the task was being run on each build for all build types. In fact, I didn’t even need to build the project. I just needed to sync the gradle files, and the task would run. This sent me digging, and I ended up learning this tiny but important difference between task execution and configuration.

That’s a TIL for this week.


  1. Here’s where I finally found the explanation. And here’s where the documentation mentions it.