The original post was sent to jzman-blog, a wechat official account. Welcome to follow and exchange.

Gradle builds tasks using Project and Task.

  • First introduction to Gradle series
  • Gradle: Groovy Basics
  • Gradle series build script basics
  • Gradle series understanding Gradle tasks
  • Gradle plugin for Gradle series
  • Gradle series of Java Gradle plug-ins
  • Gradle series of Android Gradle plugin
  • Gradle series Android Gradle base configuration
  • Android Gradle advanced Configuration
  • The Gradle series builds Maven private server libraries from scratch

Gradle is built using a series of tasks. This article introduces tasks in detail.

  1. There are multiple ways to create tasks
  2. Multiple ways to access tasks
  3. Task grouping and description
  4. The operator
  5. Task execution analysis
  6. Task scheduling
  7. Enabling and disabling tasks
  8. OnlyIf assertion for the task
  9. Task rules

There are multiple ways to create tasks

Gradle allows you to create tasks in a variety of ways. The various ways of creating tasks are reflected in the shortcuts provided by Project and the create method provided by the built-in TaskContainer. Here are some common ways of creating tasks:

/** * Method prototype: Task Task (String name) throws InvalidUserDataException; * /
// Define the Task variable to receive tasks created by the Task () method, which configures the Task created by the method
def Task taskA = task(taskA)
// Configure the created Task
taskA.doFirst {
    println "The first way to create a task"
}

/**task * Second way to create tasks: Map parameters can be configured, such as dependencies, task description, group, etc. * Prototype: task task(Map
      
        args, String name) throws InvalidUserDataException; * /
      ,?>
def Task taskB = task(group: BasePlugin.BUILD_GROUP,taskB,description: "Description")
// Configure the created Task
taskB.doLast {
    println "Second way to create a task"
    println "TaskB group: ${taskb.group}"
    println TaskB: ${taskb.description}
}

/** * The third way to create a Task is to create a Task using a closure. The delegate object in the closure is the Task. You can call all the properties and methods of the Task in the closure to configure the Task. Task task(String name, Closure configureClosure); * /
task taskC{
    description 'taskC description '
    group BasePlugin.BUILD_GROUP
    doFirst{
        println "Third way to create a task"
        println ${group}"
        println ${description}"}}/** * The fourth way to create a Task can be configured either in a closure or in a Map parameter. The parent of the closure overwrites the same configuration in the Map. > args, String name, Closure configureClosure); * /
def Task taskD = task(group: BasePlugin.BUILD_GROUP,taskD,description: "Description"){
    description 'taskD description '
    group BasePlugin.UPLOAD_GROUP
    doFirst{
        println "The fourth way to create a task"
        println ${group}"
        println "Task description: ${description}"}}Copy the code

You can select a proper method to create a Task. Task parameters can be configured in the Map. The following are available configurations in the Map:

Default value DefaultTask Overwrite: whether to replace an existing Task. This parameter is used together with type. Default valuefalseDependsOn: Configures a dependency for the current task. Default [] Action: An action or closure added to the task. Default is dependsOnnullDescription: Indicates the task description. Default valuenullGroup: task group. Default valuenull
Copy the code

Create a Task using a closure. You can use the closure to create a Task by calling all the attributes and methods of the Task.

// Use TaskContainer to create tasks
tasks.create("taskE"){
    description 'taskE's Description '
    group BasePlugin.BUILD_GROUP
    doFirst{
        println "Third way to create a task"
        println "Task group: ${group}"
        println Task description: ${description}}}Copy the code

Tasks are the attribute of a Project and its type is TaskContainer, so you can use tasks to create tasks. Of course, TaskContainer also has other methods to create tasks.

Multiple ways to access tasks

The created Task belongs to an attribute of the Project. The attribute name is the Task name and the type of the attribute is Task. If the Task name is known, you can access and manipulate the Task directly by the Task name and understand the access and manipulation of the corresponding Task object as follows:

/** * The first way to access a Task: Task name. doLast{} */
task taskF{

}
taskF.doLast{
    println "The first way to access a task"
}
Copy the code

Tasks are created using the Create method of TaskContainer, which is a collection of created tasks, In a Project, you can access TaskContainer via the Tasks attribute. The type of tasks is TaskContainer. Therefore, you can access the attributes and methods of the created task by accessing geometry elements.

/** * Second way to access tasks: use TaskContainer to access tasks */
task taskG{

}
tasks['taskG'].doLast {
    println "Second way to access tasks"
}
Copy the code

Tasks.getat (‘taskG’), the getAt() method in TaskCollection, This allows you to access and operate on related tasks by task name.

You can also access tasks through path access. There are two key ways to access tasks through path access: The difference between findByPath and getByPath is that findByPath returns null when the task cannot be found, while getByPath throws UnknowTaskException when the task cannot be found.

/** * Third way to access a task: use a path to access a task */
task taskH{
    println 'taskH'
    // Access the task through a path. The parameter can be a path.
    println tasks.findByPath(':GradleTask:taskG')
    // Access the task through the path. The parameter can be the task name
    println tasks.findByPath('taskG')
    println tasks.getByPath('taskG')}Copy the code

The execution results of the above codes are as follows:

PS E:\Gradle\study\GradleTask> gradle taskH

> Configure project :
taskH
null
task ':taskG'
task ':taskG'


FAILURE: Build failed with an exception.

* Where:
Build file 'E:\Gradle\study\GradleTask\build.gradle' line: 98

* What went wrong:
A problem occurred evaluating root project 'GradleTask'.
> Task with path 'test' not found in root project 'GradleTask'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 2s
Copy the code

In the process of using a path to access a task, the specific task cannot be accessed by writing a parameter as a path. This may be a writing problem that can be solved in the future study.

FindByName and getByName can also be accessed by task name, the main methods are findByName and getByName, the difference is the same as the third access method, code reference is as follows:

/** * The fourth way to access a task: use the task name to access */
task taskJ
tasks['taskJ'].doLast{
    println 'taskJ'
    println tasks.findByName('taskJ')
    println tasks.getByName('taskJ')}Copy the code

Gradle allows you to access tasks in different ways. You can use Gradle to access tasks in different ways.

Task grouping and description

In fact, task groups and descriptions have been mentioned and configured in previous articles. Here, it is briefly explained that task groups and descriptions are actually configured for tasks that have been created, as follows:

// Task grouping and description
def Task task1 = task taskK
task1.group = BasePlugin.BUILD_GROUP
task1.description = 'Test Task Grouping and Description'
task1.doLast {
    println "taskK is group = ${group}, description = ${description}"
}

Copy the code

The following is the result of the above code execution, for reference:

PS E:\Gradle\study\GradleTask> gradle taskK > Task :taskK taskK is group = build, BUILD SUCCESSFUL in 2s1 actionable task: 1 executed
Copy the code

As you can see from the execution result, if the task properties are configured, all information about the task can be accessed.

The operator

We can use the << operator to replace the doLast() method with the task.dolast () operator:

//<< task operator
// This is not recommended for Gradle 5.0
task taskL <<{
    println "doLast"
}
//
task taskL{
    doLast{
        println "doLast"}}Copy the code

The execution results of the above two methods are as follows:

PS E:\Gradle\study\GradleTask> gradle taskL

> Configure project :
The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead.
        at build_6syzx8ks0l09hby4j6yn247u9.run(E:\Gradle\study\GradleTask\build.gradle:123)

> Task :taskL
doLast


BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
PS E:\Gradle\study\GradleTask> gradle taskL

> Task :taskL
doLast


BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
PS E:\Gradle\study\GradleTask>
Copy the code

From the output, you can see that both methods output the desired result. In addition, the log shows that this method has been abandoned since Gradle 5.0. Therefore, it is recommended to use doLast to configure tasks.

Task execution analysis

Gradle tasks can be configured before or after a Task is executed using doFirst and doLast. When we execute a Task, we are actually executing the actions owned by the Task. You can use getActions() to retrieve all actions that can be executed. You can use @taskAction to annotate doSelf to indicate the actions that the Task should execute.

// Task execution process analysis
def Task taskA = task taskB(type: CustomTask)
taskA.doFirst {
    println "Call before Task execution: doFirst"
}

taskA.doLast {
    println "Call doLast after Task execution"
}

class CustomTask extends DefaultTask{
    @TaskAction
    def doSelf(a){
        println "Task execution itself calls: doSelf"}}Copy the code

The result of the above code is as follows:

PS E:\Gradle\study\GradleTask2> Gradle taskB > Task :taskB Task: doFirst Task: doSelf Task: doLast BUILD SUCCESSFUL in 2s1 actionable task: 1 executed
Copy the code

Since Task execution iterates through the list of actions to be executed, doFirst actions must be placed at the top of the action list and doLast actions must be placed at the bottom of the action list to ensure the order of execution. The actions corresponding to doSelf are placed in the middle of the list, so that the corresponding order of execution is guaranteed. Here is the pseudocode:

// Create a Task using the @taskAction annotation as the Task itself
// At this point, the Task is being created, and the actionList contains only actions executed by the Task itself
actionList.add(0,doSelfAction)
// After the task is created, if doFirst is set, an action for doFist will be added to the top of the actionList
// At this point, the action corresponding to doFirst adds the first part of the actionList to ensure that the doFirst method is executed before the task starts executing
actionList.add(0,doFirstAction)
// After the task is created, if doLast is set, the corresponding Action of doLast will be added to the bottom of the actionList to ensure that the doLast method is executed after the task is started
actionList.add(doLastAction)
Copy the code

The task execution process is basically as above, try to experience more in specific practice.

Task scheduling

Gradle uses two methods, shoundRunAfter and mustRunAfter, to control which Task is executed first:

/** ** taskC. ShouldRunAfter (taskD) : taskC must execute after taskD */
task taskC  {
    doFirst{
        println "taskC"
    }
}
task taskD  {
    doFirst{
        println "taskD"
    }
}
taskC.shouldRunAfter(taskD)
Copy the code

The execution result of the above code is as follows:

PS E:\Gradle\study\GradleTask2> gradle taskC taskD

> Task :taskD
taskD

> Task :taskC
taskC


BUILD SUCCESSFUL in 2s
2 actionable tasks: 2 executed
Copy the code

Enabling and disabling tasks

The Task has a enabled attribute, which can be used to enable or disable a Task. If it is set to true, the Task is enabled, and if it is disabled, it defaults to true, as shown below:

taskA.enabled = true
Copy the code

OnlyIf assertion for the task

An assertion is a conditional expression, and a Task has a onlyIf method that takes a closure as an argument. If the closure returns true, the Task will be executed, and if the closure returns true, the Task will not be executed. In this way, Task assertions control which tasks need to be executed. Here’s a packaged case column to learn about task assertions, with code reference as follows:

// onlyIf assertion for the task
final String BUILD_ALL = 'all'
final String BUILD_FIRST = 'first'
final String BUILD_OTHERS = 'others'

task taskTencentRelease{
    doLast{
        println "Play app Treasure channel package"
    }
}

task taskBaiduRelease{
    doLast{
        println "Call Baidu Mobile Assistant Channel package"
    }
}

task taskMiuiRelease{
    doLast{
        println "Tap xiaomi App Store Channel package"
    }
}

task buildTask{
    group BasePlugin.BUILD_GROUP
    description "Channel package"
}

// Add the dependent specific task for buildTask
buildTask.dependsOn taskTencentRelease, taskBaiduRelease, taskMiuiRelease

taskTencentRelease.onlyIf{
    if (project.hasProperty("buildApp")){
        Object buildApp = project.property("buildApp")
        return BUILD_ALL == buildApp || BUILD_FIRST == buildApp
    }else{
        return true
    }
}

taskBaiduRelease.onlyIf{
    if (project.hasProperty("buildApp")){
        Object buildApp = project.property("buildApp")
        return BUILD_ALL == buildApp || BUILD_FIRST == buildApp
    }else{
        return true
    }
}

taskMiuiRelease.onlyIf{
    if (project.hasProperty("buildApp")){
        Object buildApp = project.property("buildApp")
        return BUILD_OTHERS == buildApp || BUILD_ALL == buildApp
    }else{
        return true}}Copy the code

Here is the result of the above code:

PS E:\Gradle\study\GradleTask2> gradle-pBuildApp =first buildTask > Task :taskBaiduRelease :taskTencentRelease BUILD SUCCESSFUL in 1s2 actionable tasks: 2Executed PS E:\Gradle\study\GradleTask2> gradle-pbuildApp = Others buildTask > Task :taskMiuiRelease SUCCESSFUL in 1s1 actionable task: 1Executed PS E:\Gradle\study\GradleTask2> gradle-pbuildApp =all buildTask > Task :taskBaiduRelease Task :taskTencentRelease BUILD SUCCESSFUL in 1s3 actionable tasks: 3 executed
Copy the code

It can be seen that when we execute buildTask, we configure the attribute buildApp for Project, and achieve the customized packaging strategy of different channel packages with different buildApp values and onlyIf, which can be used for reference in actual development.

In addition, notice how the above code execution command is written as follows:

// Where buildApp and = others are key-value pairs. -p can be used when executing tasks with commands
// -p specifies the key pair of k-v attributes for the current Project, i.e. -pk =V
gradle -PbuildApp=others buildTask
Copy the code

Task rules

Create a task that contains a task, you can create a task from the Project attribute of the name of the task, you can use the TaskContain addRule method to add the corresponding task rules, reference code is as follows:

// Task rules
tasks.addRule("A description of this rule") {// Closures often use -> as a separator between arguments and code blocks
    String taskName ->
        task(taskName) {
            doLast{
                println ${taskName} does not exist
            }
        }
}

task taskTest{
    dependsOn taskX
}
Copy the code

The result of the above code:

PS E:\Gradle\study\GradleTask2> Gradle taskTest > Task :taskX taskX1 actionable task: 1 executed
Copy the code

If you do not specify a particular case, an error will be reported. If you do, a message will be displayed.