创建一个task

创建一个task很简单,直接使用task关键字来创建,最常见的形式是使用task + 名字 + Closure代码块。示例如下。

task ms1 {println("=================task ms1 added=================")
}

也可以只有task + 名字,就像下面这样。当然这样的task什么也不做。

task myTask

创建task经常还会看到另外一种写法,在名字后面加上 << 符号。

task hello << {println 'Hello world!'
}

这种写法和下面的写法是等同的,也就是在创建task的时候为其添加了一个action到末尾。不过这种写法目前已被标记为deprecated,预期会在Gradle 5.0版本中移除,所以最好不要再使用了。

task hello {doLast {println 'Hello world!'}
}

上述几种写法都有一个共同点,就是创建task的时候必须指定一个名字,每个task都需要有一个名字,所有的task在创建之后都隶属于某个Project。同一个Project中任意两个task的名字不能相同。

此外,添加task不一定要在build.gradle的最外层,可以放在任意的代码块中,只是在执行时机上有所区别。

如下代码将task hello放到了allprojects()中执行,它会为项目中每一个project创建一个名为hello的task。

allprojects {task hello
}

如下代码则在循环中创建了task0,task1,task2,task3四个不同的task。

4.times { index ->task "task$index" {println "I'm task number $index"}
}

创建task的时候还可以指定一些参数。比较重要的有:

  1. type: task类型
  2. dependsOn:依赖的task列表
  3. group:task所属的组

其他参数可以参见 https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskContainer.html#create-java.util.Map-

为task添加Action

每个task中可以包含0到多个Action,这些Action保存在一个ArrayList成员变量中,当执行这个task时,会按照顺序依次执行所有的Action。

要为task添加Action,可以通过task的doFirst()和doLast()方法来实现。doFirst()方法会将Action添加到ArrayList的开头,而doLast()方法则将Action添加到ArrayList的末尾。可以有多个doFirst()和doLast(),配置task时会按照它们出现的先后顺序添加到Action列表中。

创建task的同时添加Action

可以在创建task的同时添加Action,只需要将其添加到创建task的闭包中即可。

以下代码在创建ms1 task的时候为它添加了4个action。

task ms1 {doFirst {println("=================ms1 doFirst1=================")}doFirst {println("=================ms1 doFirst2=================")}doLast {println("=================ms1 doLast1=================")}doLast {println("=================ms1 doLast2=================")}
}-- 执行这个task会产生如下输出 --
=================ms1 doFirst2=================
=================ms1 doFirst1=================
=================ms1 doLast1=================
=================ms1 doLast2=================

为现有task增加Action

也可以为已有的task增加Action。

如下代码为build task增加了两个Action,如果其他没有对build task再做修改,那么这里添加的doFirst会作为build task的第一个Action最先被执行,而doLast则会作为build task的最后一个Action,在其他Action执行完成后再执行。

build {doFirst {println("=================build doFirst=================")}doLast {println("=================build doLast=================")}
}

如下代码为ms1 task添加了一个新的action,这个action被添加在了其他action的前面。

ms1.doFirst {println("=================ms1 doFirst=================")
}

为动态创建的task增加Action

有些task是动态创建的,无法直接在build.gradle的最外层为其增加Action,又或者需要在task定义代码之前为其添加Action,这时可以将其放到Project.afterEvaluate()方法中去执行。

afterEvaluate {assembleDebug {doLast {println("=================assembleDebug doLast=================")}}
}

或者可以在Task creation事件的回调中去添加。

tasks.whenTaskAdded { task ->if (task.name == 'assembleDebug') {task.doFirst {println("=====================assembleDebug doFirst=========================")}}
}

从上面的三小节可以看出,添加action可以出现在代码任意地方,只要拿到对应的task就可以为其添加action。需要注意的是不能为正在运行的task添加action,否则会抛出异常。例如如下代码为myTask添加了一个action,在这个action执行的时候再为myTask添加另一个action,这时就会有运行时异常,但可以在一个task执行的时候为其他task添加action,这里将myTask改成任意其他的task名字是可以正常运行的。

myTask.doFirst {println("=================ms1 doFirst=================")myTask.doFirst {println("=================add another action=================")}
}

指定Action名字

从Gradle 4.2版本开始支持在调用doFirst和doLast的时候为Action指定一个名字,不过这个特性仍然是Incubating状态,也就是试验阶段。关于Incubating状态的说明参见 https://docs.gradle.org/current/userguide/feature_lifecycle.html#sec:states 。

关于actionName这个参数是这样描述的。

actionName - An arbitrary string that is used for logging.

也就是说这个名字应当只用在打印log的时候。

添加task之间的依赖

task提供了dependsOn,finalizedBy方法来管理task之间的依赖关系,依赖关系表达的是执行这个task时所需要依赖的其他task,也就是说这个task不能被单独执行,执行这个task之前或之后需要执行另外的task。

在一个task前执行另一个task

要在一个task之前执行另一个task,可以通过配置dependsOn的依赖关系实现。

例如有两个task:taskA和taskB,通过指定taskA.dependsOn taskB就可以让在执行taskA之前先执行taskB,或者指定taskB.dependsOn taskA就可以让在执行taskB之前先执行taskA。

在一个task后执行另一个task

要在一个task之后执行另一个task,无法通过配置dependsOn实现。要实现这个功能需要通过finalizedBy来实现。

同样的有两个task:taskA和taskB,通过指定taskA.finalizedBy taskB就可以让在执行taskA之后执行taskB,或者指定taskB.finalizedBy taskA就可以让在执行taskB之后先执行taskA。

在一个task中执行另一个task

gradle在执行一组task的时候会根据他们的依赖关系生成一个task序列,然后按照序列的先后顺序来依次执行各个task。单个task的执行一定是一个原子过程,gradle不允许在一个task中执行另一个task,因此不会出现嵌套执行的情况。也就是说所有的task执行都是由gradle自身去调度,只有执行完一个task之后才会去执行下一个task,不会出现task没有执行完就转去执行另一个task中的任务的情况。

值得一提的是,现有gradle版本中为task保留了一个execute()方法来执行一个task,这个方法已被标记为deprecated,不过还是可以用的。

如下代码看起来是在ms2 task的最后一个action中去执行ms1 task,但实际上在执行gradlew ms2的时候,仍然会先执行ms1,然后再执行ms2,并不会出现执行ms2到最后一个action的时候,再转去执行ms1。所以它和添加ms2到ms1的依赖没有区别,鉴于这种用法已被标记为deprecated,且过程难以被理解,所以最好就不要用execute()了。

task ms2 {doLast {println("=====================ms2.=========================")ms1.execute()}}task ms1 {doLast {println("=====================ms1.=========================")}
}

tasks队列

在通过dependsOn和finalizedBy设定好task之间的依赖关系后,在执行一个task时就会根据依赖关系生成一个task队列。

例如,有四个task,task0,task1,task2,task3。指定依赖关系如下。

task0 dependsOn task1
task1 dependsOn task2
task0 finalizedBy task3

当执行gradlew task0时就会先生成 task2 -> task1 -> task0 -> task3这样一个序列,然后按照顺序来执行。

给tasks排序

在上述例子中,执行task0得到的tasks队列中每个task的位置都是明确的,它们的先后顺序也是完全确定的。然而更多的情况是,一个任务队列中有部分task的位置不是很明确。

例如,有四个task,task0,task1,task2,task3。指定依赖关系如下。

task0 dependsOn task1
task0 dependsOn task2
task0 finalizedBy task3

按照依赖关系,执行task0生成的任务队列可以是task2 -> task1 -> task0 -> task3,也可以是task1 -> task2 -> task0 -> task3,可以看到task2和task1之间的顺序是不确定的。虽然gradle总是可以按照一定的规则(例如task名字的字典序)来得到一个固定的task队列,但有时我们需要人为明确这个顺序,例如这里我们希望task2总是在task1之前执行。这时有两种方案,一种是使用dependsOn,我们让task1 dependsOn task2,这样task2一定会在task1之前执行,但这种方案会让单独执行task1的时候也会先执行task2,这有可能和实际需要不符。这时就需要使用另一种方案,通过shouldRunAfter和mustRunAfter来指定这个顺序。

shouldRunAfter和mustRunAfter表达的都是一个任务需要在另一个任务之后执行,它们的区别是mustRunAfter要求gradle生成任务队列时必须确保这个顺序一定要满足,如果不能满足就会报错,shouldRunAfter相当于建议gradle按照这顺序来生成任务队列,gradle会优先参考其他约束条件,如果在满足其他约束条件后,这条约束也能满足,那么就采纳这个顺序,如果不能满足,就忽略这个请求。

例如有依赖关系task0 dependsOn task1,这时又指定task1 mustRunAfter task0,显然这时这个条件无法被满足,因此执行task0的时候会报错。但如果指定task1 shouldRunAfter task0,则不会有错误,gradle会自动忽略掉这条请求。

再来看上述例子
task0 dependsOn task1
task0 dependsOn task2
task0 finalizedBy task3

这时无论指定task1 shouldRunAfter task2,还是task1 mustRunAfter task2,都会得到任务队列task2 -> task1 -> task0 -> task3。

需要注意的是,无论是shouldRunAfter还是mustRunAfter,影响的只是task在队列中的顺序,并不影响任何任务间的执行依赖,也就是说使用shouldRunAfter和mustRunAfter并不会导致任务队列中添加新的task。

例如指定ms1 mustRunAfter ms2,如果ms1和ms2没有其他依赖关系,那么在执行ms1的时候并不会先执行ms2,执行ms2之后也不会执行ms1。

预定义的gradle task

gradle为每个工程提供了很多预先定义好的task,通过这些task我们可以不需要手动创建任何task就可以实现编译,打包,测试等功能。在前文中已经出现的clean build等就是gradle中预定义的task。在做Android开发时,Android Gradle插件也提供了一组预定义的gradle task,当我们新建一个Android工程就能看到如下所示的task列表,这里的tasks都是gradle和Android Gradle Plugin为我们创建好的,可以直接使用。

清理工程(clean task)

clean task用来清理上次编译的缓存内容

编译相关task

和编译相关的task主要有:build和assemble,其中build依赖assemble,也就是说执行build之前会先执行assemble。在Android上,会根据buildType和productFlavor的不同自动创建多个assembleXxx任务,如assembleDebug,assembleRelease等,assemble会依赖所有的assembleXxx任务,也就是说执行assemble会先执行assembleDebug,assembleRelease等一系列的assemble任务。

在编译完成后执行一段代码

有时我们需要在编译任务完成后执行某一个任务,按照通常的做法,我们创建一个task,然后将这个task放到编译的task之后执行。

如前所述要在一个task之后执行另一个task,可以通过finalizedBy来实现,但是编译任务对应的task很多,有些还是动态创建的,如果每个任务都指定一遍finalizedBy会很麻烦。好在gradle提供了一个buidlFinished()方法,它可以在编译完成后执行一定的操作,不用理会当前执行的是哪个编译task,只需要把task中代码移到buidlFinished中即可。

project.gradle.buildFinished {println "build finished"
}

和buildFinished()对应的还有一个buildStarted()方法,它可以在编译开始之前执行需要的代码。

task相关属性

获取当前task的名字

要在当前任务里获取自己的名字,可以使用其name属性

task ms1{doLast {println name            // ms1}
}

获取当前action的名字

如前所述,action的名字应当只用在内部记录log的时候,目前没有找到方法获取到这个名字。

获取当前运行的gradle任务列表

在gradle脚本中可以通过project.gradle.startParameter.taskNames来获取本次执行的gradle task列表。它得到的是一个数组,即使只执行了一个task。如果没有指定运行任何task,则返回空的数组。

-- build.gradle --
println project.gradle.startParameter.taskNames-- 命令行 --
> gradlew clean build-- 输出 --
[clean, build]

自定义任务属性

可以在任务配置代码中,通过ext.xxx来为任务添加自定义属性。

例如

task myTask {ext.myProperty = "myValue"
}

定义好之后就可以在其他代码中通过myTask.myProperty来访问这个属性的值。

默认任务

Gradle 允许在build.gradle脚本中定义一个或多个默认任务,默认任务的意思是没有指定任何任务的时候会执行默认任务。要指定默认任务可以使用defaultTasks关键字。

例如,如下代码指定默认任务为myTask和build,则执行gradlew时如果没有指定任何任务,就会执行myTask和build。

defaultTasks 'myTask', 'build'task myTask {doLast {println "run default task."}
}

需要注意的是,使用defaultTasks指定默认任务的时候,任务名一定要加引号,即使它是在任务定义之后声明,也是如此。

覆盖已有的任务

有时我们可能需要覆盖掉一个已有的任务,这个任务可能是自定义的,或者是来自其他Gradle插件中的任务。虽然这种场景并不常见,不过还是有必要了解这种用法,说不定在需要的时候就能帮上大忙。

覆盖已有任务只需要重新创建一个新的同名task,并指定overwrite为true即可。

overwrite的覆盖操作,不仅会覆盖掉原先task的所有action,而且会覆盖原先task指定的type类型,还会覆盖掉所有和原先task相关的依赖关系。被覆盖的依赖关系不仅包含原先的task对其他task的依赖,还包含其他task对原先task的依赖,所有的和原先task相关的依赖关系都会被移除。当然,可以在overwrite之后重新定义新的依赖关系。

overwrite的覆盖操作不会覆盖原先创建task块中的代码,所有创建task代码仍然会被执行。

此外,如果定义overwrite的task在这之前并没有被创建,那么gradle会忽略这个属性,等同于创建一个新的task,不会有错误出现。

如下代码创建了两个名为copy的task,第二个copy task指定了overwrite为true,因此第二个copy task会覆盖第一个。此外,第一个copy task依赖一个名为delete的task,由于这条依赖关系在overwrite之前配置,所以同样会被覆盖。

task delete(type: Delete) {delete "s1.txt"
}task copy(type: Copy) {println namefrom(file('ss.txt'))into(file('new_dir'))doLast {println "I am a copy task"}
}copy.dependsOn deletetask copy(overwrite: true) {println namedoLast {println('I am the new one.')}
}

这里执行gradlew copy的完整流程如下。

  1. 创建delete task,执行语句delete “s1.txt”,注意这里只是执行了delete()方法指定了要删除的文件,并没有真正执行删除操作,删除操作只有执行这个task的时候,才会在delete task的action中执行。
  2. 创建第一个copy task,依次执行所有代码,即println name, from(file(‘ss.txt’)),into(file(‘new_dir’))和doLast。同样的这里的doLast只是为task添加了action,并不是真正的执行了这个action。
  3. 执行copy.dependsOn delete为copy添加一个依赖关系。
  4. 创建第二个copy task,由于overwrite为true,所以会移除原先的copy task,原先copy task所有信息都会丢失,包括依赖关系,包括指定的type,也包括 from(file(‘ss.txt’)),into(file(‘new_dir’))的配置和doLast添加的action。但是println name由于已经执行过了,已经打印出来的内容不会消失不见。然后重新创建新的copy task,执行println name和doLast。
  5. 执行copy task,由于原先task已经不存在,所以只会执行println(‘I am the new one.’)这个action中的语句。不会执行println “I am a copy task”,也不会执行复制’ss.txt’到new_dir的操作,也不会执行delete task。最终结果就是打印了两次name和一次I am a copy task。

Android Gradle Task详解相关推荐

  1. Android Gradle 自定义Task详解二:进阶

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78523958 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  2. Android Gradle 自定义Task 详解

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76408024 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  3. 史上最全Android build.gradle配置详解

    Android Studio是采用gradle来构建项目的,gradle是基于groovy语言的,如果只是用它构建普通Android项目的话,是可以不去学groovy的.当我们创建一个Android项 ...

  4. Android Studio build.gradle配置详解

    Android Studio是采用gradle来构建项目的,gradle是基于groovy语言的,如果只是用它构建普通Android项目的话,是可以不去学groovy的.当我们创建一个Android项 ...

  5. Android build.gradle文件详解(转述自《Android第一行代码》第二版)

    Android build.gradle文件详解 1. 最外层目录下的build.gradle文件 1.1 repostories 1.2 dependencies 2. app目录下的build.g ...

  6. Android Studio 插件开发详解四:填坑

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78265540 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  7. Android Studio 插件开发详解三:翻译插件实战

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78113868 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  8. Android Studio 插件开发详解一:入门练手

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78112003 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  9. Gradle Wrapper 详解

    Gradle Wrapper 详解 我们介绍了 Android 项目的目录及 Gradle 配置,我们提到有个目录是/gradle/wrapper.今天这篇文章我们来学习 Gradle Wrapper ...

最新文章

  1. 机器学习与高维信息检索 - Note 5 - (深度)前馈神经网络((Deep) Feedforward Neural Networks)及基于CVXOPT的相关实例
  2. 为啥我从后台查到的值在页面显示的是undefined_再谈一个管理后台列表功能应有的素质...
  3. 【若依(ruoyi)】datetimepicker日期和时间插件
  4. C# ReaderWriterLockSlim类
  5. python 柱状图 间距_专题第18篇:Python 绘图入门
  6. 在 2017 年将会更加流行的 6 个 Web 开发趋势
  7. eazyui ajax传值,jquery easyui ajax data属性传值方式
  8. 小米手机将终结卡顿?未来所有机型将升级新MIUI
  9. Boston波士顿房价数据下载
  10. VS2010SP1dvd1安装失败。
  11. php mysql 博客,PHP操作MySQL
  12. QQ在线客服代码(绝对强制对话框)
  13. 静态代理和动态代理区别(是否有实现类)
  14. C++ 类成员函数的函数指针
  15. redis分布式锁实现(以抢红包为例)
  16. vue.jsv-html,Vue.js-资料-组件化思想 —下
  17. 华清远见重庆中心学习-受控组件,非受控组件
  18. python3如何下载模块
  19. java自学笔记(day14)归纳总结自:B站狂神说java
  20. 计算机水平每分钟多少字,电脑打字每分钟打多少在一才算快啊? 一般拼音打字一分钟多少才算可以?...

热门文章

  1. 热力图绘制热力图 matplotlib
  2. 分布式文件系统之DFS复制、命名空间和NameNode
  3. Java各运算符详解
  4. 关于Windows10 文件名最大长度260
  5. Ubuntu系统React-native Android开发watchman安装
  6. python控制相机自动拍照_python控制gphoto2自动拍照
  7. 【sv】复制/拼接操作符
  8. MP代码生成器(拿来即用)
  9. R语言 COVID-19综合案例
  10. 录屏软件的鼻祖是techSmith 的喀秋莎 Camtasia Studio2023功能解锁及安装教程录屏视频编辑软件