Gradle executes the procedure

1. Initialization phase 2. Configuration phase 3

The initialization phase actually corresponds to our settings.gradle file. The configuration phase is basically to build up the dependencies between our tasks and our tasks which is a directed acyclic graph of who executes first and who executes last. The execution phase is when Gradle finally builds your project

Task definition and configuration

The most important attribute in the definition of a task is the group attribute. Many people ignore this attribute, resulting in the definition of all tasks in the default Other group, which is difficult to find.

You can also look at the total number of tasks that can be set

Note the timing of the execution of the task code, for example

Task helloTask {println 'helloTask1 'doFirst {println("helloTask2")}} task helloTask {println 'helloTask1' doFirst {println("helloTask2")}Copy the code

Let’s look at the results

That is, the statements you write directly in the task are executed in the config phase. Only things written in doFirst or Dolast packages are executed in gradle execution

Statistics the duration of assembleDefaultDebug

With that in mind, let’s do a simple feature that counts the time it takes to build a task. To count its elapsed time we must know where the assembleDefaultDebug task started. We can just type the build command and see which task to start from

You can see from the figure above that this starts with the preBuild task.

this.afterEvaluate { Project project ->
    def preTask = project.tasks.getByName('preBuild')
    def t1
    preTask.doFirst {
        t1 = System.currentTimeMillis()
    }
    def buildTask = project.tasks.getByName('assembleDefaultDebug')
    buildTask.doLast {
        println("build takes:" + (System.currentTimeMillis() - t1))
    }
}

Copy the code

Let’s look at the results

Task dependent on

I’m not going to repeat some of the dependencies that you can find all over the Internet, but I’m going to talk about some that others haven’t.

First, a task can depend on multiple tasks. It does not mean that a task can only depend on one task

task taskX{
    doLast {
        println "taskX"
    }
}
task taskY{
    doLast {
        println "taskY"
    }
}

task taskZ(dependsOn:[taskX,"assembleDebug"]){
    doLast {
        println "taskZ"
    }
}

Copy the code

In addition, taskZ relies on Task X and Task Y in the above example, so don’t assume that just because I wrote Task X before taskY that X was executed before Y. There are a lot of people on the Internet who say that taskX and taskY are executed in random order, but from my own testing results, it seems that this is not the case. Instead, taskX and taskY are based on the names of the taskX and taskY, starting with the first letter and executing the larger letters first. Maybe it’s because my Gradle version is too high? Anyway, you should be careful here, do more tests, and communicate with me on the message board

In addition, tasks can be dynamically dependent. For example, you can rely on tasks that conform to certain rules.

DependsOn this.tasks.findall {task -> return task.name. StartsWith ("build")} doLast {dependsOn this.tasks.findall {task -> return task.name. println "taskZ" } }Copy the code

Task INPUT output

Let’s first look at the output of the program

You can see that only output from files or file paths is supported.

In addition to file and file path input, map type input is also supported. Note that the map value is object, which means that any type can be supported.

To make a small function, we output our Android minSdk and versionSdk and so on into a file.

ext { mMinSdkVersion = 26 mTargetSdkVersion = 29 infoFile = file(this.projectDir.toString() + "/info.txt") if (! infoFile.exists()) { infoFile.createNewFile() } }Copy the code

Define version and then the output file.

Look at the write file task

task writeInfoTask() {
    inputs.property("mMinSdkVersion", this.mMinSdkVersion.toString())
    inputs.property("mTargetSdkVersion", this.mTargetSdkVersion.toString())
    outputs.files this.infoFile
    doLast {
        println("writeInfoTask")
        def data = inputs.getProperties()
        File file = outputs.getFiles().getSingleFile()
        file.write(data.toString())
    }
}
task zreadInfoTask() {
    inputs.files this.infoFile
    doLast {
        println("readInfoTask")
        File file = inputs.getFiles().getSingleFile()
        println("file content:" + file.text)
    }
}

Copy the code

Finally, let’s perform this task

task taskX {
    dependsOn(writeInfoTask, zreadInfoTask)
    doLast {
        println("task all over")
    }
}
Copy the code

Notice why I’m calling it zredInfoTask instead of readInfoTask? Gradle can decide intelligently if the input of a task depends on the output of another task. Gradle can intelligently put the output of the task in front of the input file and the input task in back. But according to my tests here, that’s not the case. Try it out for yourself and see if the gradle version causes this feature to be different.

There is also a mustRunAfter function that guarantees execution after a task has finished. Those interested can explore on their own.