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

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

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

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

多种方式创建任务

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

/**
* 第一种创建任务方式:
* 方法原型: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 创建任务也有创建任务的其他构造方法,到此关于任务的创建就基本介绍完了。

多种方式访问任务

创建的任务(Task)属于项目(Project)的一个属性,其属性名就是任务名,该属性的类型是 Task,如果已知任务名称,那么可以通过任务名直接访问和操纵该任务了,也可以理解访问和操纵该任务所对应的 Task 对象,参考

如下:

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

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

/**
* 访问任务的第二种方式:使用TaskContainer访问任务
*/
task taskG{ }
tasks['taskG'].doLast {
println "第二种访问任务的方式"
}

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

还可以通过路径访问的方式访问任务,通过路径访问任务有两个关键方法: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),一起交流学习。

Gradle系列之认识Gradle任务的更多相关文章

  1. Gradle系列之Android Gradle插件

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

  2. Gradle系列之Android Gradle基础配置

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

  3. Gradle系列之Android Gradle高级配置

    本篇文章主要在之前学习的基础上,从实际开发的角度学习如何对 Android Gradle 来进行自定义以满足不同的开发需求,下面是 Gradle 系列的几篇文章: Gradle系列之初识Gradle ...

  4. Gradle系列之初识Gradle

    原文首发于微信公众号:躬行之(jzman-blog) 学习 Android 有一段时间了,开发中经常使用到 Gradle ,但是不知道 Gradle 构建项目的原理,计划花一点时间学习一下 Gradl ...

  5. Gradle系列之Groovy基础篇

    原文发于微信公众号 jzman-blog,欢迎关注交流. 上一篇学习了 Gradle 的入门知识,Gradle 基于 Groovy,今天学习一下 Groovy 的基础知识,Groovy 是基于 JVM ...

  6. Gradle系列之构建脚本基础

    原文发于微信公众号 jzman-blog,欢迎关注交流. 前面两篇文章分别介绍了 Gradle 基础知识以及 Groovy 相关基础知识,这也是学习 Gradle 所必需了解的,文章链接如下:: Gr ...

  7. Gradle系列之从零搭建Maven私服库

    原文发于微信公众号 jzman-blog,欢迎关注交流. 前面几篇文章学习了 Gradle 相关知识,Gradle 系列文章如下: Gradle系列之初识Gradle Gradle之Groovy基础篇 ...

  8. [转]Android Studio系列教程六--Gradle多渠道打包

    转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...

  9. Android Studio系列教程六--Gradle多渠道打包

    Android Studio系列教程六--Gradle多渠道打包 2015 年 01 月 15 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzh ...

随机推荐

  1. IDEA开发工具使用 git 创建项目、拉取分支、合并分支

    转载自:https://blog.csdn.net/qq_39470733/article/details/80366435 工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下: 假设小组中有 ...

  2. Egret游戏大厅制作思路

    Egret游戏大厅制作思路 Egret中,写好的代码最终都被打包到main.js里面,只有库文件会单独生成出来,按需加载. 游戏中有需求,要将一些游戏(或者模块)进行外包,然后从主游戏大厅中进入,那么 ...

  3. 《UNIX环境高级编程》(APUE) 笔记第十一章 - 线程

    11 - 线程 Github 地址 1. 线程概念 典型的 UNIX进程 可以看成只有一个 控制线程 :一个进程在某一时刻只能做一件事情.有了 多个控制线程 ,就可以把进程设计成在某一时刻能够做不止一 ...

  4. Ubuntu apt update卡在Connecting to security.ubuntu.com解决方法

    Ubuntu操作系统运行apt update命令时会卡在Connecting to security.ubuntu.com,搭了梯子也无法解决 尝试了网络上的方法,如: https://blog.cs ...

  5. 05 . k8s实战之部署PHP/JAVA网站

    传统部署和k8s部署区别 通常使用传统的部署的时候,我们一个web项目,网站的搭建,往往使用的如下的一种整体架构,可能有的公司在某一环节使用的东西是不一样,但是大体的框架流程是都是差不多的 1111 ...

  6. Passing Reference Data Type Arguments

    public void moveCircle(Circle circle, int deltaX, int deltaY) { // code to move origin of circle to ...

  7. 手写SpringMVC框架(二)-------结构开发设计

    续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...

  8. Isset、empty、count、is_null的比较

    1.empty判断变量是否为空, 先把变量转为布尔值再返回:对变量(字符串.数组等)赋值为一切为空的值.或者未定义的变量都返回true,即判断为空,比如null,' ',0,array(),false ...

  9. java语言进阶(一)_Object类_常用API

    第一章 Object类 1.1 概述 java.lang.Object类是Java语言中的根类,即所有类的父类.它中描述的所有方法子类都可以使用.在对象实例化的时候,最终找的父类就是Object. 如 ...

  10. 面试之Hashtable和ConcurrentHashMap

    那么要如何保证HashMap的线程安全呢? 方法有很多,比如使用Hashtable或者Collections.synchronizedMap,但是这两位选手都有一个共同的问题:性能.因为不管是读还是写 ...