Gradle(二)任务

一、创建任务

以一个任务名字创建任务

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 方式找不到任务会抛出 UnknownTaskExceptionfindByPath 方式找不到任务会返回 null

通过名称方式访问任务

def myTask = task("myTask")
tasks.getByName("myTask").doLast {
    println "任务名称 === ${name}"
}
tasks.findByName("myTask").doLast {
    println "任务名称 === ${name}"
}
复制代码

getByName 方式找不到任务会抛出 UnknownTaskExceptionfindByName 方式找不到任务会返回 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个渠道,华为、小米是首发包,而 OppoVivo 是非首发包,通过命令指定打哪个渠道包,比如说

//打所有包
> gradle -q -PbuildApk=all buildChannelApk
打华为渠道包
打Oppo渠道包
打Vivo渠道包
打小米渠道包
//打首发包
> gradle -q -PbuildApk=first buildChannelApk
打华为渠道包
打小米渠道包
//打非首发包
> gradle -q -PbuildApk=non-first buildChannelApk
打Oppo渠道包
打Vivo渠道包
复制代码

命令行中 -PProject 指定 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执行之后执行
复制代码

从上面打印日志来看,为了达到 doFirstdoSelfdoLast 这样一个执行顺序,就必须把 doFirstaction 放到列表最前面,doSelfaction 放到列表中间,doLastaction 放到列表最后面。当任务创建时,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 了,再看下 doFirstdoLast 实现

@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 顺序执行的目的

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享