目录
一、多种方式创建任务
1. 以任务名称创建任务
- 示例:
// 以任务名称创建任务
def Task taskCreatedByName = task taskCreatedByName
// def Task taskCreatedByName = task(taskCreatedByName)
taskCreatedByName.doLast {
println "这是一个通过任务名称创建的任务"
}
- 原型:
Task task(String name) throws InvalidUserDataException;
2. 以任务名称和对任务配置的 Map 对象创建任务
- 示例:
// 以任务名称和对任务配置的 Map 对象创建任务
def Task taskCreatedByNameAndMap = task taskCreatedByNameAndMap,group:BasePlugin.BUILD_GROUP
taskCreatedByNameAndMap.doLast {
println "这是一个通过任务名称和对任务配置的 Map 对象创建的任务"
}
- 原型:
Task task(Map<String, ?> args, String name) throws InvalidUserDataException;
- Map 对象的可用配置
配置项 | 描述 | 默认值 |
---|---|---|
type | 基于一个存在的 Task 来创建,与继承差不多 | DefaultTask |
overwrite | 是否替换存在的 Task,与 type 配合使用 | false |
dependsOn | 配置任务依赖 | [] |
action | 添加到任务中的一个 Action 或者一个闭包 | null |
description | 配置任务的描述 | null |
group | 配置任务的分组 | null |
3. 以任务名称和闭包配置创建任务
- 通过 Map 对象可以进行配置的项是有限的,所以可以通过闭包的形式来对任务进行更加灵活全面的配置
- 示例:
// 以任务名称和闭包配置创建任务
task taskCreatedByNameAndClosure {
doLast {
println "这是一个通过任务名称和闭包配置创建的任务"
}
}
- 原型:
Task task(String name, Closure configureClosure);
4. 以任务名称、Map 参数和闭包配置创建任务
- 示例:
// 以任务名称,Map 对象和闭包配置创建任务
task taskCreatedByNameMapAndClosure,group:BasePlugin.BUILD_GROUP,{
doLast {
println "这是一个通过任务名称,Map 对象和闭包配置创建任务"
}
}
- 原型:
Task task(Map<String, ?> args, String name, Closure configureClosure);
5. TaskContainer 对象的 create 方法创建任务
- 使用 TaskContainer 对象创建任务的方式也较多,可以查看 Project.java 文件看看不同的创建方式,这里仅举例一种
- 示例:
// TaskContainer 对象的 create 方法创建任务
tasks.create('taskCreatedByTaskContainer') {
doLast {
println "这是一个通过 TaskContainer 对象创建的任务"
}
}
- 原型:
Task create(String name, Closure configureClosure) throws InvalidUserDataException;
二、多种方式访问任务
1. 以任务名称访问任务
我们创建的任务都会成为 Project 的一个属性,属性名称就是任务名称,所以可以通过任务名称来访问操作任务
task task1
task1.doLast {
println "以任务名称访问任务"
}
2. 以访问集合元素访问任务
任务都是通过 TaskContainer 对象创建的,在 Project.java 中 tasks 就是 TaskContainer 的对象,所以可以通过 tasks 来获取对应的任务
示例代码中“[]”并不表示 tasks 是一个 Map,而是 Groovy 重载了这个操作符,跟进源码会发现最后是调用的 findByName(String name)
实现的
task task2
// TaskContainer getTasks();
tasks['task2'].doLast {
println "以访问集合元素访问任务"
}
3. 通过路径访问
通过路径访问的方式有两种:get 与 find
区别:get 找不到任务的时候会抛出异常而 find 找不到任务时会返回 null
task task3
tasks['task3'].doLast {
println "-----通过路径访问任务-----"
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. 通过名称访问
通过名称访问的方式有两种:get 与 find
区别:get 找不到任务的时候会抛出异常而 find 找不到任务时会返回 null
task task4
tasks['task4'].doLast {
println "-----通过名称访问任务-----"
println tasks.findByName('task2')
println tasks.getByName('task2')
println tasks.findByName('task0')
// println tasks.getByPath('task0')
}
// TaskCollection.java
// TaskContainer 继承自 TaskCollection<Task> 和 PolymorphicDomainObjectContainer<Task>
// getByName 方法来自于 TaskCollection
@Override
T getByName(String name) throws UnknownTaskException;
// NamedDomainObjectCollection.java
// TaskCollection 继承 NamedDomainObjectSet
// NamedDomainObjectSet 继承 NamedDomainObjectCollection
// findByName 方法来自于 NamedDomainObjectCollection
@Nullable
T findByName(String name);
5. 值得注意的是,通过路径访问任务的时候,参数值可以是路径也可以是任务名称,但是通过名称访问任务的时候,参数值就只可以是任务名称
三、任务分组与描述
- 任务分组就是对任务进行分类,方便我们对任务归类整理
- 任务描述就是对任务进行一个说明,方便他人清楚任务的用途
task task5
task5.group = BasePlugin.BUILD_GROUP
task5.description = "这是一个 build 分组的任务"
task5.doLast {
println "group:$group,description:$description"
}
- 使用
gradle tasks
命令可以查看任务的信息,包括任务的分组和描述
- 在 AS 中 Gradle Task 列表可以看见任务的分组,鼠标悬停某一任务上可以看见它的描述
四、任务的执行分析
- 任务的执行其实就是任务里面一系列的 Action 的执行
// AbstractTask.java
@Override
public List<ContextAwareTaskAction> getTaskActions() {
if (actions == null) {
actions = new ArrayList<ContextAwareTaskAction>(3);
}
return actions;
}
- 通过源码来看任务的执行(版本号:7.0.2)
- 示例
task executeTask(type: Task6)
executeTask.doFirst {
println "Task 自身执行之前执行 doFirst"
}
executeTask.doLast {
println "Task 自身执行之后执行 doLast"
}
class Task6 extends DefaultTask {
@TaskAction
def doSelf(){
println "Task 自身执行 doSelf"
}
}
- 源码分析之 doSelf() 方法—创建任务的时候,Gradle 会解析任务中带有
@TaskAction
注解标注的方法,作为任务执行的 Action,然后通过prependParallelSafeAction(...)
方法将这个 Action 添加到 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(…) 方法—永远都是在 Action 执行列表的第一位进行插入操作,重点
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(…) 方法—永远都是在 Action 执行列表的最后进行插入操作,重点
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;
}
五、任务排序
说是排序,其实不准确,应该说是指定任务 A 在任务 B 执行后执行,并且根据调用的 api 不一样,执行与否有可能和一定的区分
shouldRunAfter
应该在之后执行mustRunAfter
必须在之后执行
task taskA
taskA.doLast{
println "TaskA 执行..."
}
task taskB
taskB.doLast{
println "TaskB 执行..."
}
taskA.mustRunAfter taskB
六、任务的启用和禁用
enabled 属性,默认为 true 表示启用任务,置为 false 则会禁用该任务
task task7
task7.doLast{
println "Task7 执行..."
}
task7.enabled = false
七、任务的 onlyIf 断言
- 什么是 onlyIf 断言:就是一个条件表达式。Task 类中有一个
onlyIf
方法,这个方法接收一个闭包参数,根据返回值 true/false 来决定任务是执行还是跳过 - 示例:
task task8
task8.onlyIf{
println "Task8 执行..."
false
}
task task9
task9.onlyIf{
println "Task9 执行..."
true
}
八、任务规则
- 通过第二小节的学习,我们知道了多种访问任务的方式,当我们通过任务名称来访问任务时,如果对应的任务不存在,那么就会调用我们添加的规则来处理这种情况
// 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);
}
- 示例:
tasks.addRule "这是任务规则的描述",{ String taskName ->
task(taskName).doLast{
println "$taskName 不存在,无法执行..."
}
}
- 没有添加规则时的没有找到任务会编译失败
- 添加规则后的没有找到任务会根据规则执行(示例为打印信息)
整理学习自飞雪无情大佬的《Android Gradle 权威指南》和互联网资料