一、创建任务
以一个任务名字创建任务
def myTask = task("myTask")
复制代码
它调的是 Project 接口的方法
Task task(String name) throws InvalidUserDataException;
复制代码
以一个任务参数 Map 对象 + 一个任务名字创建任务
def myTask = task(group: BasePlugin.BUILD_GROUP, "myTask")
复制代码
它调的是 Project 接口的方法
Task task(Map<String, ?> args, String name) throws InvalidUserDataException;
复制代码
参数 Map 配置如下
选项 | 说明 | 默认值 |
---|---|---|
type | 基于一个存在的 Task 来创建 | DefaultTask |
overwrite | 是否替换存在的 Task | false |
dependsOn | 任务依赖 | [] |
action | 添加到任务的一个闭包 | null |
description | 任务描述 | null |
group | 任务分组 | null |
以一个任务名字 + 闭包创建任务
def myTask = task("myTask", { description "创建任务" })
复制代码
它调的是 Project 接口的方法
Task task(String name, Closure configureClosure);
复制代码
第二种方式 Map 参数项比较有限, 而以闭包方式配置会更灵活,因为闭包里的委托对象就是 Task,所以可调用 Task 的任何内容
以 tasks 关联方法创建任务
def myTask = tasks.create("myTask")
复制代码
它调的是 Project 接口的方法 getTasks() 来获取 tasks
TaskContainer getTasks();
复制代码
TaskContainer 是一个接口,其 create 方法与前三种方式是一样的,因为前三种方式的底层源码最终调的还是 TaskContainer 接口的 create 方法
二、访问任务
通过任务对象方式访问任务
def myTask = task("myTask")
myTask.doLast {
println "任务名称 === ${name}"
}
复制代码
通过类似集合访问方式访问任务
def myTask = task("myTask")
tasks["myTask"].doLast {
println "任务名称 === ${name}"
}
复制代码
tasks[] 方式,查看源代码最终调的是 findByName
通过路径方式访问任务
def myTask = task("myTask")
tasks.getByPath("myTask").doLast {
println "任务名称 === ${name}"
}
tasks.findByPath("myTask").doLast {
println "任务名称 === ${name}"
}
复制代码
getByPath 方式找不到任务会抛出 UnknownTaskException,findByPath 方式找不到任务会返回 null
通过名称方式访问任务
def myTask = task("myTask")
tasks.getByName("myTask").doLast {
println "任务名称 === ${name}"
}
tasks.findByName("myTask").doLast {
println "任务名称 === ${name}"
}
复制代码
getByName 方式找不到任务会抛出 UnknownTaskException,findByName 方式找不到任务会返回 null
三、任务依赖
依赖关系可控制任务执行的先后顺序,一个任务可同时依赖多个任务
def myTask1 = task("myTask1") {
doLast {
println "myTask1"
}
}
def myTask2 = task("myTask2") {
doLast {
println "myTask2"
}
}
//myTask3依赖myTask1,myTask2
def myTask3 = task(dependsOn: [myTask1, myTask2], "myTask3") {
doLast {
println "myTask3"
}
}
//myTask4依赖myTask1,myTask2,myTask3
def myTask4 = task("myTask4") {
dependsOn([myTask1, myTask2, myTask3])
doLast {
println "myTask4"
}
}
复制代码
四、任务分组和描述
任务分组是对任务的分类,方便管理任务,任务描述是说明这个任务的作用
def myTask = task("myTask") {
group BasePlugin.BUILD_GROUP
description "myTask"
doLast {
println "group === ${group}, description === ${description}"
}
}
复制代码
五、任务排序
类似任务依赖方式
def myTask1 = task("myTask1") {
doLast {
println "myTask1"
}
}
def myTask2 = task("myTask2") {
doLast {
println "myTask2"
}
}
def myTask3 = task("myTask3") {
doLast {
println "myTask3"
}
}
myTask1.shouldRunAfter(myTask2)
myTask3.mustRunAfter(myTask1)
//gradle -q myTask1 myTask2 myTask3
//执行结果
//myTask2
//myTask1
//myTask3
复制代码
A.shouldRunAfter(B) 表示任务 A 应该在任务 B 之后执行,只是应该而已,不一定是这个顺序;A.mustRunAfter(B) 表示任务 A 必须在任务 B 之后执行
六、任务启用和禁用
任务禁用后,该任务会被跳过
def myTask = task("myTask") {
doLast {
println "myTask"
}
}
myTask.enabled = false //任务跳过后,不会有任何输出信息
复制代码
七、任务断言
Task 有一个 onlyIf 方法,其参数是一个闭包,如果闭包返回 true 则表示该任务执行,否则跳过
final String APK_FOR_FIRST = "first" //首发包
final String APK_FOR_NON_FIRST = "non-first" //非首发包
final String APK_FOR_ALL = "all" //所有包
final String ORDER_FLAG = "buildApk" //打包命令标识
def buildHWChannelApk = task("buildHWChannelApk") {
doLast {
println "打华为渠道包"
}
}
buildHWChannelApk.onlyIf {
boolean isBuildApk = false
if (project.hasProperty(ORDER_FLAG)) {
def property = project.property(ORDER_FLAG)
if (APK_FOR_FIRST == property ||
APK_FOR_ALL == property) {
isBuildApk = true
}
}
isBuildApk
}
def buildXiaoMiChannelApk = task("buildXiaoMiChannelApk") {
doLast {
println "打小米渠道包"
}
}
buildXiaoMiChannelApk.onlyIf {
boolean isBuildApk = false
if (project.hasProperty(ORDER_FLAG)) {
def property = project.property(ORDER_FLAG)
if (APK_FOR_FIRST == property ||
APK_FOR_ALL == property) {
isBuildApk = true
}
}
isBuildApk
}
def buildOppoChannelApk = task("buildOppoChannelApk") {
doLast {
println "打Oppo渠道包"
}
}
buildOppoChannelApk.onlyIf {
boolean isBuildApk = false
if (project.hasProperty(ORDER_FLAG)) {
def property = project.property(ORDER_FLAG)
if (APK_FOR_NON_FIRST == property ||
APK_FOR_ALL == property) {
isBuildApk = true
}
}
isBuildApk
}
def buildVivoChannelApk = task("buildVivoChannelApk") {
doLast {
println "打Vivo渠道包"
}
}
buildVivoChannelApk.onlyIf {
boolean isBuildApk = false
if (project.hasProperty(ORDER_FLAG)) {
def property = project.property(ORDER_FLAG)
if (APK_FOR_NON_FIRST == property ||
APK_FOR_ALL == property) {
isBuildApk = true
}
}
isBuildApk
}
task("buildChannelApk") {
dependsOn(["buildHWChannelApk", "buildXiaoMiChannelApk", "buildOppoChannelApk", "buildVivoChannelApk"])
group BasePlugin.BUILD_GROUP
description "打渠道包"
}
复制代码
上面代码定义了4个渠道,华为、小米是首发包,而 Oppo、Vivo 是非首发包,通过命令指定打哪个渠道包,比如说
//打所有包
> gradle -q -PbuildApk=all buildChannelApk
打华为渠道包
打Oppo渠道包
打Vivo渠道包
打小米渠道包
//打首发包
> gradle -q -PbuildApk=first buildChannelApk
打华为渠道包
打小米渠道包
//打非首发包
> gradle -q -PbuildApk=non-first buildChannelApk
打Oppo渠道包
打Vivo渠道包
复制代码
命令行中 -P 为 Project 指定 K-V 格式的键值对
八、任务规则
当获取的任务不存在,可添加规则处理异常
tasks.addRule("规则描述", { String taskName ->
task(taskName) {
doLast {
println "该${taskName}任务不存在,请查证后再执行"
}
}
})
task("myTask") {
dependsOn missTask
}
复制代码
九、任务简单分析
当执行一个任务时,实则是在遍历一个actions列表
private List<ContextAwareTaskAction> actions;
@Override
public List<InputChangesAwareTaskAction> getTaskActions() {
if (actions == null) {
//创建一个actions列表
actions = new ArrayList<InputChangesAwareTaskAction>(3);
}
return actions;
}
复制代码
为啥这么说?举个例子
class MyTask extends DefaultTask {
@TaskAction
def doSelf() {
println "MyTask执行自己"
}
}
def myTask = task(type: MyTask, "myTask") {
doFirst {
println "MyTask执行之前执行"
}
doLast {
println "MyTask执行之后执行"
}
}
//打印日志如下
//MyTask执行之前执行
//MyTask执行自己
//MyTask执行之后执行
复制代码
从上面打印日志来看,为了达到 doFirst、doSelf、doLast 这样一个执行顺序,就必须把 doFirst 的 action 放到列表最前面,doSelf 的 action 放到列表中间,doLast 的 action 放到列表最后面。当任务创建时,Gradle 会解析带有 @TaskAction 的注解,通过 prependParallelSafeAction 方法把 action 添加到列表
@Override
public void prependParallelSafeAction(final Action<? super Task> action) {
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
//添加TaskActionWrapper对象到actions列表第一位,调用wrap方法
getTaskActions().add(0, wrap(action));
}
private InputChangesAwareTaskAction wrap(final Action<? super Task> action) {
//调用Warp重载方法
return wrap(action, "unnamed action");
}
private InputChangesAwareTaskAction wrap(final Action<? super Task> action, String actionName) {
if (action instanceof InputChangesAwareTaskAction) {
return (InputChangesAwareTaskAction) action;
}
//创建一个TaskActionWrapper对象
return new TaskActionWrapper(action, actionName);
}
private static class TaskActionWrapper implements InputChangesAwareTaskAction {
private final Action<? super Task> action;
private final String maybeActionName;
public TaskActionWrapper(Action<? super Task> action, String maybeActionName) {
this.action = action;
this.maybeActionName = maybeActionName;
}
@Override
public void execute(Task task) {
ClassLoader original = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(action.getClass().getClassLoader());
try {
action.execute(task);
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
......
}
复制代码
现在列表已经有本身的 action 了,再看下 doFirst、doLast 实现
@Override
public Task doFirst(final Closure action) {
hasCustomActions = true;
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
taskMutator.mutate("Task.doFirst(Closure)", new Runnable() {
@Override
public void run() {
//添加进actions列表第一位
getTaskActions().add(0, convertClosureToAction(action, "doFirst {} action"));
}
});
return this;
}
@Override
public Task doLast(final Closure action) {
hasCustomActions = true;
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
taskMutator.mutate("Task.doLast(Closure)", new Runnable() {
@Override
public void run() {
//添加进actions列表最后一位
getTaskActions().add(convertClosureToAction(action, "doLast {} action"));
}
});
return this;
}
private InputChangesAwareTaskAction convertClosureToAction(Closure actionClosure, String actionName) {
//创建ClosureTaskAction对象
return new ClosureTaskAction(actionClosure, actionName, getServices().get(UserCodeApplicationContext.class).current());
}
private static class ClosureTaskAction implements InputChangesAwareTaskAction {
private final Closure<?> closure;
private final String actionName;
@Nullable
private final UserCodeApplicationContext.Application application;
private ClosureTaskAction(Closure<?> closure, String actionName, @Nullable UserCodeApplicationContext.Application application) {
this.closure = closure;
this.actionName = actionName;
this.application = application;
}
@Override
public void execute(Task task) {
if (application == null) {
doExecute(task);
} else {
application.reapply(() -> doExecute(task));
}
}
private void doExecute(Task task) {
closure.setDelegate(task);
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
ClassLoader original = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(closure.getClass().getClassLoader());
try {
if (closure.getMaximumNumberOfParameters() == 0) {
closure.call();
} else {
closure.call(task);
}
} catch (InvokerInvocationException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw e;
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
......
}
复制代码
从上面代码可知,doFirst 永远在列表第一位,doLast 永远在列表末尾,从而达到了这三个 action 顺序执行的目的