Task of Gradle learning

Posted by skehoe on Sun, 13 Feb 2022 14:29:29 +0100

1, Create tasks in multiple ways

1. Create task with task name

  • Example:
// Create task with task name
def Task taskCreatedByName = task taskCreatedByName
// def Task taskCreatedByName = task(taskCreatedByName)

taskCreatedByName.doLast {
	println "This is a task created by task name"
}
  • Prototype:
Task task(String name) throws InvalidUserDataException;

2. Create a task with the task name and the Map object configured for the task

  • Example:
// Create a task with the task name and the Map object configured for the task
def Task taskCreatedByNameAndMap = task taskCreatedByNameAndMap,group:BasePlugin.BUILD_GROUP

taskCreatedByNameAndMap.doLast {
	println "This is a task configuration by task name and Map Object creation task"
}
  • Prototype:
Task task(Map<String, ?> args, String name) throws InvalidUserDataException;
  • Available configurations for Map objects
Configuration itemdescribeDefault value
typeCreate based on an existing Task, which is similar to inheritanceDefaultTask
overwriteReplace the existing Task and use it with typefalse
dependsOnConfigure task dependencies[]
actionAn Action or closure added to the tasknull
descriptionDescription of the configuration tasknull
groupConfigure grouping of tasksnull

3. Create a task with the task name and closure configuration

  • There are limited items that can be configured through Map objects, so tasks can be configured more flexibly and comprehensively in the form of closures
  • Example:
// Create a task with the task name and closure configuration
task taskCreatedByNameAndClosure {
	doLast {
		println "This is a task created by task name and closure configuration"
	}
}
  • Prototype:
Task task(String name, Closure configureClosure);

4. Create a task with task name, Map parameters and closure configuration

  • Example:
// Create a task with task name, Map object and closure configuration
task taskCreatedByNameMapAndClosure,group:BasePlugin.BUILD_GROUP,{
	doLast {
		println "This is a task name, Map Object and closure configuration creation task"
	}
}
  • Prototype:
Task task(Map<String, ?> args, String name, Closure configureClosure);

5. create method of taskcontainer object

  • There are also many ways to create tasks using TaskContainer objects. You can view project Look at the different creation methods of java files. Here is only one example
  • Example:
// The create method of the TaskContainer object creates a task
tasks.create('taskCreatedByTaskContainer') {
	doLast {
		println "This is a pass TaskContainer Object creation task"
	}
}
  • Prototype:
Task create(String name, Closure configureClosure) throws InvalidUserDataException;

2, Multiple ways to access tasks

1. Access the task by task name

The tasks we create will become an attribute of Project. The attribute name is the task name, so you can access the operation task through the task name

task task1

task1.doLast {
	println "Access task by task name"
}

2. Access tasks by accessing collection elements

Tasks are created through the TaskContainer object in project Tasks in Java is the object of TaskContainer, so you can get the corresponding tasks through tasks

"[]" in the sample code does not mean that tasks is a Map, but that Groovy overloads this operator. Follow up the source code and you will find that it is finally implemented by the called findByName(String name)

task task2
// TaskContainer getTasks();
tasks['task2'].doLast {
	println "Access tasks by accessing collection elements"
}

3. Access via path

There are two ways to access by path: get and find
Difference: get throws an exception when it cannot find a task, while find returns null when it cannot find a task

task task3

tasks['task3'].doLast {
	println "-----Access tasks via path-----"
	println tasks.findByPath(':task2')
	println tasks.getByPath(':task2')
	println tasks.findByPath(':task0')
	println tasks.getByPath(':task0')
}
// TaskContainer.java
@Nullable
Task findByPath(String path);

Task getByPath(String path) throws UnknownTaskException;

4. Access by name
There are two ways to access by name: get and find
Difference: get throws an exception when it cannot find a task, while find returns null when it cannot find a task

task task4

tasks['task4'].doLast {
	println "-----Access tasks by name-----"
	println tasks.findByName('task2')
	println tasks.getByName('task2')
	println tasks.findByName('task0')
	// println tasks.getByPath('task0')
}
// TaskCollection.java
// TaskContainer inherits from taskcollection < task > and polymorphicdomainobjectcontainer < task >
// The getByName method comes from TaskCollection
@Override
T getByName(String name) throws UnknownTaskException;
// NamedDomainObjectCollection.java
// TaskCollection inherits NamedDomainObjectSet
// NamedDomainObjectSet inherits NamedDomainObjectCollection
// The findByName method comes from the NamedDomainObjectCollection
@Nullable
T findByName(String name);

5. It should be noted that when accessing a task through a path, the parameter value can be either the path or the task name, but when accessing a task through a name, the parameter value can only be the task name

3, Task grouping and description

  1. Task grouping is to classify tasks, which is convenient for us to classify and sort out tasks
  2. Task description is a description of the task to facilitate others to understand the purpose of the task
task task5
task5.group = BasePlugin.BUILD_GROUP
task5.description = "This is a build Grouped tasks"
task5.doLast {
	println "group:$group,description:$description"
}

  1. Use the gradle tasks command to view task information, including task grouping and description
  2. In AS, task grouping can be seen in the Gradle Task list, and its description can be seen by hovering over a task

4, Task execution analysis

  1. The execution of a task is actually the execution of a series of actions in the task
// AbstractTask.java
@Override
public List<ContextAwareTaskAction> getTaskActions() {
    if (actions == null) {
        actions = new ArrayList<ContextAwareTaskAction>(3);
    }
    return actions;
}
  1. See the execution of the task through the source code (version number: 7.0.2)
  • Examples
task executeTask(type: Task6)
executeTask.doFirst {
	println "Task Execute before self execution doFirst"
}
executeTask.doLast {
	println "Task Execute after self execution doLast"
}

class Task6 extends DefaultTask {
	@TaskAction
	def doSelf(){
		println "Task Self execution doSelf"
	}
}

  • doSelf() method of source code analysis - when creating a task, Gradle will parse the method marked with @ TaskAction annotation in the task as the Action executed by the task, and then use prependParallelSafeAction(...) Method to add the Action to the execution list of the Action
// AbstractTask.java
@Override
public void prependParallelSafeAction(final Action<? super Task> action) {
    if (action == null) {
        throw new InvalidUserDataException("Action must not be null!");
    }
    getTaskActions().add(0, wrap(action));
}
  • doFirst(...) method of source code analysis - always insert at the first place in the Action execution list, focusing on gettaskactions() add(0, wrap(Action, actionName));
// AbstractTask.java
@Override
public Task doFirst(final Action<? super Task> action) {
    return doFirst("doFirst {} action", action);
}

@Override
public Task doFirst(final String actionName, final Action<? super Task> action) {
    hasCustomActions = true;
    if (action == null) {
        throw new InvalidUserDataException("Action must not be null!");
    }
    taskMutator.mutate("Task.doFirst(Action)", new Runnable() {
        @Override
        public void run() {
            getTaskActions().add(0, wrap(action, actionName));
        }
    });
    return this;
}
  • doLast(...) method of source code analysis - always insert at the end of the Action execution list, focusing on gettaskactions() add(wrap(Action, actionName));
// AbstractTask.java
@Override
public Task doLast(final Action<? super Task> action) {
    return doLast("doLast {} action", action);
}

@Override
public Task doLast(final String actionName, final Action<? super Task> action) {
    hasCustomActions = true;
    if (action == null) {
        throw new InvalidUserDataException("Action must not be null!");
    }
    taskMutator.mutate("Task.doLast(Action)", new Runnable() {
        @Override
        public void run() {
            getTaskActions().add(wrap(action, actionName));
        }
    });
    return this;
}

5, Task sorting

It's not accurate to say sorting. It should be said that task A is specified to execute after task B is executed, and whether it is executed or not may be different according to the api called

  • Shouldranafter should be executed after
  • mustRunAfter must be executed after
task taskA
taskA.doLast{
	println "TaskA implement..."
}

task taskB
taskB.doLast{
	println "TaskB implement..."
}

taskA.mustRunAfter taskB

6, Enabling and disabling tasks

Enabled attribute. The default value is true, which means that the task is enabled. If it is set to false, the task will be disabled

task task7
task7.doLast{
	println "Task7 implement..."
}
task7.enabled = false

7, onlyIf assertion for task

  1. What is an onlyIf assertion: it is a conditional expression. There is an onlyIf method in the Task class. This method receives a closure parameter and determines whether to execute or skip the Task according to the return value of true/false
  2. Example:
task task8
task8.onlyIf{
	println "Task8 implement..."
	false
}

task task9
task9.onlyIf{
	println "Task9 implement..."
	true
}

8, Task rules

  1. adopt Section II We know a variety of ways to access tasks. When we access tasks by task name, if the corresponding task does not exist, we will call the rules we added to deal with this situation
// DefaultNamedDomainObjectCollection.java
@Override
public T findByName(String name) {
    T value = findByNameWithoutRules(name);
    if (value != null) {
        return value;
    }
    ProviderInternal<? extends T> provider = index.getPending(name);
    if (provider != null) {
        // TODO - this isn't correct, assumes that a side effect is to add the element
        provider.getOrNull();
        // Use the index here so we can apply any filters to the realized element
        return index.get(name);
    }
    if (!applyRules(name)) {
        return null;
    }
    return findByNameWithoutRules(name);
}
  1. Example:
tasks.addRule "This is the description of the task rule",{ String taskName ->
	task(taskName).doLast{
		println "$taskName Does not exist and cannot be executed..."
	}
}
  • If no rule is added, the task not found will fail to compile
  • The task not found after adding the rule will be executed according to the rule (for example, print information)

Organize and study the Android Gradle authoritative guide and Internet materials from the ruthless boss of Feixue

Topics: Android Gradle Groovy Task