Gradle系列之认识Gradle任务

原文发于微信公众号 jzman-blog,欢迎关注交流。java

前面几篇学习了 Gradle 构建任务的基础知识,了解了 Project 和 Task 这两个概念,建议先阅读前面几篇文章:微信

Gradle 的构建工做是有一系列的 Task 来完成的,本文将针对 Task 进行详细介绍,本文主要内容以下:闭包

  1. 多种方式建立任务
  2. 多种方式访问任务
  3. 任务分组和描述
  4. 操做符
  5. 任务的执行分析
  6. 任务排序
  7. 任务的启用和禁用
  8. 任务的onlyIf断言
  9. 任务规则

多种方式建立任务

Gradle 中可使用多种方式来建立任务,多种建立任务的方式最终反映在 Project 提供的快捷方法以及内置的 TaskContainer 提供的 create 方法,下面是几种常见的建立任务的方式:post

/** * 第一种建立任务方式: * 方法原型:Task task(String name) throws InvalidUserDataException; */
//定义Task变量接收task()方法建立的Task,方法配置建立的Task
def Task taskA = task(taskA)
//配置建立的Task
taskA.doFirst {
    println "第一种建立任务的方式"
}

/**task * 第二种建立任务方式:可在Map参数中进行相关配置,如依赖、任务描述、组别等 * 方法原型:Task task(Map<String, ?> args, String name) throws InvalidUserDataException; */
def Task taskB = task(group: BasePlugin.BUILD_GROUP,taskB,description: "描述")
//配置建立的Task
taskB.doLast {
    println "第二种建立任务的方式"
    println "任务taskB分组:${taskB.group}"
    println "任务taskB描述:${taskB.description}"
}

/** * 第三种建立任务方式:经过闭包的方式建立Task,闭包里的委托对象就是Task,便可在闭包内调用Task * 的一切属性和方法来进行Task的配置 * 方法原型:Task task(String name, Closure configureClosure); */
task taskC{
    description 'taskC的描述'
    group BasePlugin.BUILD_GROUP
    doFirst{
        println "第三种建立任务的方式"
        println "任务taskC分组:${group}"
        println "任务taskC描述:${description}"
    }
}

/** * 第四种建立任务的方式:可在闭包中灵活配置,也可在Map参数中配置,闭包中中的配置父覆盖Map中相同的配置 * 方法原型:Task task(Map<String, ?> args, String name, Closure configureClosure); */
def Task taskD = task(group: BasePlugin.BUILD_GROUP,taskD,description: "描述"){
    description 'taskD的描述'
    group BasePlugin.UPLOAD_GROUP
    doFirst{
        println "第四种建立任务的方式"
        println "任务taskD分组:${group}"
        println "任务taskD描述:${description}"
    }
}
复制代码

上面是建立任务的四种方式,使用时选择合适的建立方式便可,上面提到 Map 中能够配置 Task 的相关参数,下面是是 Map 中可以使用的配置:学习

type:基于一个已存在的Task来建立,相似于类的继承,默认值DefaultTask
overwrite:是否替换存在的Task,通常和type配合使用,默认值false
dependsOn:配置当前任务的依赖,默认值[]
action:添加到任务中的一个Action或者是一个闭包,默认值为null
description:任务描述,默认值null
group:任务分组,默认值null
复制代码

经过闭包的方式建立 Task,闭包里的委托对象就是 Task,便可在闭包内调用 Task 的一切属性和方法来进行 Task 的配置,能够说使用闭包的这种任务建立方式更灵活,此外还可使用 TaskContainer 建立任务,参考以下:测试

//使用TaskContainer建立任务的方式
tasks.create("taskE"){
    description 'taskE的描述'
    group BasePlugin.BUILD_GROUP
    doFirst{
        println "第三种建立任务的方式"
        println "任务taskE分组:${group}"
        println "任务taskE描述:${description}"
    }
}
复制代码

tasks 是 Project 的属性,其类型是 TaskContainer,因此能够经过 tasks 来建立任务,固然 TaskContainer 建立任务也有建立任务的其余构造方法,到此关于任务的建立就基本介绍完了。gradle

多种方式访问任务

建立的任务(Task)属于项目(Project)的一个属性,其属性名就是任务名,该属性的类型是 Task,若是已知任务名称,那么能够经过任务名直接访问和操纵该任务了,也能够理解访问和操纵该任务所对应的 Task 对象,参考 以下:ui

/** * 访问任务的第一种方式:Task名称.doLast{} */
task taskF{

}
taskF.doLast{
    println "第一种访问任务的方式"
}
复制代码

任务都是经过 TaskContainer 的 create 方法建立的,而 TaskContainer 是建立任务的集合,在 Project 中可经过 tasks 属性访问 TaskContainer ,tasks 的类型就是 TaskContainer,因此对于已经建立的任务可经过访问几何元素的方式访问已建立任务的属性和方法,参考代码以下:lua

/** * 访问任务的第二种方式:使用TaskContainer访问任务 */
task taskG{

}
tasks['taskG'].doLast {
    println "第二种访问任务的方式"
}
复制代码

在 Groovy 中 [] 也是一个操做符,上面 tasks['taskG'] 的真正含义是 tasks.getAt('taskG') , getAt() 方法在 TaskCollection 中的方法,这样能够经过任务名称对相关任务进行访问和操做。spa

还能够经过路径访问的方式访问任务,经过路径访问任务有两个关键方法:findByPath 和 getByPath,区别在于前者找不到指定任务的时候会返回 null,后者找不到任务的时候会抛出 UnknowTaskException 异常,代码参考以下:

/** * 访问任务的第三种方式:使用路径访问任务 */
task taskH{
    println 'taskH'
    //经过路径访问任务,参数能够是路径(没有访问成功,写法以下)
    println tasks.findByPath(':GradleTask:taskG')
    //经过路径访问任务,参数能够是任务名称
    println tasks.findByPath('taskG')
    println tasks.getByPath('taskG')
}
复制代码

上述代码执行结果参考以下:

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
复制代码

使用路径访问任务的过程,参数写成路径访问不到具体的任务,多是写法问题,但愿在后面的学习中可以解决。

此外,还能够经过任务名称访问,方法主要是 findByName 和 getByName,区别和第三种访问方式同样, 代码参考以下:

/** * 访问任务的第四种方式:使用任务名称访问 */
task taskJ
tasks['taskJ'].doLast{
    println 'taskJ'
    println tasks.findByName('taskJ')
    println tasks.getByName('taskJ')
}

复制代码

上面学习了访问任务的四种方式,经过对 Gradle 访问任务的了解,在具体的项目构建上在结合上面访问任务的方式灵活使用。

任务分组和描述

对于任务分组及描述实际上在以前的文章已经提到过且配置过,这里再简单说明一下,任务分组和描述实际上就是对已经建立的任务配置分组和任务描述,以下面这样配置:

//任务分组与描述
def Task task1 = task taskK
task1.group = BasePlugin.BUILD_GROUP
task1.description = '测试任务分组与描述'
task1.doLast {
    println "taskK is group = ${group}, description = ${description}"
}

复制代码

下面是上述代码执行结果,参考以下:

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

> Task :taskK
taskK is group = build, description = 测试任务分组与描述


BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
复制代码

从执行结果能够看出,若是配置了任务的相关属性,那么就能够访问到任务的全部信息了。

操做符

学习一个操做符 << ,以前的测试代码中为了测试都会调用 task.doLast() 方法,咱们可使用 << 操做符来代替 doLast 方法,也就是说 daLast() 方法能够这样写:

//<< 任务操做符
//简写方式,Gradle 5.0 开始以不推荐这种写法
task taskL <<{
    println "doLast"
}
//推荐写法
task taskL{
    doLast{
        println "doLast"
    }
}
复制代码

上述两种写法的执行结果参考以下:

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>
复制代码

从输出结果能够看到两种写法都输出了想要的结果,同时观察日志发现这种简写方式已经在 Gradle 5.0 开始已经被放弃,因此推荐搭建使用 doLast 的方式配置任务。

任务的执行分析

在 Gradle 任务执行过程当中,咱们能够经过 doFirst 和 doLast 在任务执行以前或执行以后进行任务相关配置,当咱们执行一个任务的时候,其实是在执行该 Task 所拥有的 action,能够经过 getActions() 方法获取全部能够执行的 action,下面自定义一个 Task 类型 CustomTask ,并使用注解 @TaskAction 标注方法 doSelf 表示 Task 自己要执行的方法,代码以下:

//任务执行流程分析
def Task taskA = task taskB(type: CustomTask) taskA.doFirst {
    println "Task执行以前调用:doFirst"
}

taskA.doLast {
    println "Task执行以后调用:doLast"
}

class CustomTask extends DefaultTask{
    @TaskAction
    def doSelf(){
        println "Task执行自己调用:doSelf"
    }
}
复制代码

上述代码的执行结果以下:

PS E:\Gradle\study\GradleTask2> gradle taskB

> Task :taskB
Task执行以前调用:doFirst
Task执行自己调用:doSelf
Task执行以后调用:doLast


BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
复制代码

因为 Task 的执行是在遍历须要执行的 action 列表,为了保证执行的顺序,则必须将 doFirst 对应的 action 放在 action 列表的最前面,doLast 对应的 action 放在 action 列表的最后面,doSelf 对应的 action 放置在列表的中间位置,这样就能保证对应的执行顺序了,下面是伪代码:

//建立任务的时候将使用@TaskAction标注的方法做为Task自己执行的Task
//此时,任务正在建立,actionList里面只有Task自己执行的Action
actionList.add(0,doSelfAction)
//任务建立完成以后,若是设置了doFirst则会在actionList最前面添加doFist对应的action
//此时,doFirst对应的action添加actionList的最前面,保证了doFirst方法在任务开始执行以前执行
actionList.add(0,doFirstAction)
//任务建立完成以后,若是设置了doLast则会在actionList最后面添加doLast对应的action,保证了doLast方法在任务开始执行以后执行
actionList.add(doLastAction)
复制代码

任务执行的流程基本如上,尽可能在具体实践中多体会。

任务排序

Gradle 中任务排序使用到的是 Task 的两个方法 shoundRunAfter 和 mustRunAfter,能够方便的控制两个任务谁先执行:

/** * 任务顺序 * taskC.shouldRunAfter(taskD):表示taskC要在taskD的后面执行 * taskC.mustRunAfter(taskD):表示taskC必需要在taskD的后面执行 */
task taskC  {
    doFirst{
        println "taskC"
    }
}
task taskD  {
    doFirst{
        println "taskD"
    }
}
taskC.shouldRunAfter(taskD)
复制代码

上述代码的执行结果,参考以下:

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

> Task :taskD
taskD

> Task :taskC
taskC


BUILD SUCCESSFUL in 2s
2 actionable tasks: 2 executed
复制代码

任务的启用和禁用

Task 中有个 enabled 属性,可使用该属性启用和禁用某个任务,设置为 true 则启用该任务,反之则禁用该任务,该属性默认为 true,使用以下所示:

taskA.enabled = true
复制代码

任务的onlyIf断言

断言是一个条件表达式, Task 对象有一个 onlyIf 方法,该方法能够接收一个闭包做为参数,若是该闭包内参数返回 true,则该任务执行,反之则不执行该任务,这样能够经过任务的断言来控制那些任务须要执行,下面经过一个打包的案列来学习任务的断言,代码参考以下:

//任务的onlyIf断言
final String BUILD_ALL = 'all'
final String BUILD_FIRST = 'first'
final String BUILD_OTHERS = 'others'

task taskTencentRelease{
    doLast{
        println "打应用宝渠道包"
    }
}

task taskBaiduRelease{
    doLast{
        println "打百度手机助手渠道包"
    }
}

task taskMiuiRelease{
    doLast{
        println "打小米应用商店渠道包"
    }
}

task buildTask{
    group BasePlugin.BUILD_GROUP
    description "打渠道包"
}

//为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
    }
}
复制代码

下面是上述代码的执行结果:

PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=first buildTask

> Task :taskBaiduRelease
打百度手机助手渠道包

> Task :taskTencentRelease
打应用宝渠道包


BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=others buildTask

> Task :taskMiuiRelease
打小米应用商店渠道包


BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=all buildTask

> Task :taskBaiduRelease
打百度手机助手渠道包

> Task :taskMiuiRelease
打小米应用商店渠道包

> Task :taskTencentRelease
打应用宝渠道包


BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 executed
复制代码

能够看出,当咱们执行 buildTask 时为 Project 配置了属性 buildApp,经过 buildApp 不一样的值,借助 onlyIf 实现了不一样渠道包的定制打包策略,可在实际开发中借鉴加以使用。

此外,注意上述代码执行命令的写法,具体以下:

//其中buildApp和=后面的值others是键值对的关系,使用命令执行任务时可以使用-P命令简写
//-P要为当前Project指定K-V的属性键值对,即-PK=V
gradle -PbuildApp=others buildTask
复制代码

任务规则

建立的任务都是在 TaskContain 里面,我么能够经过从 Project 的属性 tasks 中根据任务的名称来获取想要获取的任务,能够经过 TaskContain 的 addRule 方法添加相应的任务规则,参考代码以下:

//任务规则
tasks.addRule("对该规则的一个描述"){
    //在闭包中经常将->做为参数与代码块之间的分隔符
    String taskName ->
        task(taskName) {
            doLast{
                println "${taskName} 不存在"
            }
        }
}

task taskTest{
    dependsOn taskX
}
复制代码

上述代码的执行结果:

PS E:\Gradle\study\GradleTask2> gradle taskTest

> Task :taskX
taskX 不存在


BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
复制代码

若是不指定对某些特殊状况的任务处理,则会报错,若是处理了则会输出相关的提示信息,Gradle 任务的了解和学习就到此为止。能够关注公众号:零点小筑(jzman-blog),一块儿交流学习。

相关文章
相关标签/搜索