前置准备
参考Gradle从0入门到实战系列【三】build.gradle之Project的前置准备工作,创建好demo项目
定义任务
定义task有很多种方式,但我们记住常用的几种即可
定义task闭包
//直接声明 task taskOne{ println "method1" } //参数定义名称 task('taskTwo'){ println "method2" } //使用tasks集合属性,引用了TaskContainer的一个实例对象。我们还可以使用Project.getTasks() tasks.create('taskThree'){ println "method3" } //使用with tasks.with(){ println "methodContainer" } //使用withType声明 tasks.withType(Test){ print "method5" } //使用register,register执行的是延迟创建。也就是说只有当task被需要使用的时候才会被创建。 tasks.register('taskSix'){ println "method6" } //这种写法gradle5.0就已经废弃 task deprecatedTask << { println "execute deprecatedTask" }
TaskContainer
解析
tasks实际上就是TaskContainer
的实例对象的映射属性,TaskContainer
实现了TaskCollection
和PolymorphicDomainObjectContainer
,所以tasks具有以上2个类的所有行为,其构建如下public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task>{ //跟进去就会发现TaskCollection和PolymorphicDomainObjectContainer中有很多关于维护task的方法的定义 }
上述Container中可实现的重要方法有如下几个
定位task
findByPath:如果没找到会返回null
task say println tasks.findByPath('say').path println tasks.findByPath(':say').path
getByPath:如果没找到会抛出UnknownTaskException
task say println tasks.getByPath('say').path println tasks.getByPath(':say').path
创建task
- 直接创建,使用create,上述定义章节有使用
延迟创建使用register
tasks.register('taskSix'){ println "method6" }
跟进源码定义如下
TaskProvider<Task> register(String var1, Action<? super Task> var2) throws InvalidUserDataException;
register返回了一个TaskProvider,和java多线程中的callable类似,当我们调用Provider.get()或TaskCollection.getByName(String)获取task的时候,才会去创建这个task。
- 替换task(使用不多)
replace的作用就是创建一个新的task,并且替换掉同样名字的老的task。
给task配置group和描述
//group可以将任务分组,在右侧的task列表中能够清晰直观的看到group task taskDev1{ group "dev" description "项目描述" println "method1" } task('taskDev2'){ group "dev" println "method2" } task taskTest{ group "test" println "method1" }
doFirst/doLast体验
一个Task包含若干Action。Task有doFirst和doLast两个函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包。//在task闭包体内声明 task taskDev1{ //doFirst:task执行时,最开始的操作 doFirst{ println "first exec" } //doLast:task执行时,最后的操作 doLast{ println "last exec" } group "dev" println "method1" } //也可以这样写 task myTask { println "config myTask" } myTask.doFirst { println "before execute myTask" } myTask.doLast { println "after execute myTask" }
任务详细使用
定义Task的时候是可以指定很多参数的,如下所示:
参数 | 含义 | 默认值 |
---|---|---|
name | task的名字 | 不能为空,必须指定 |
type | task的“父类” | DefaultTask |
overwrite | 是否替换已经存在的task | false |
dependsOn | task依赖的task的集合 | [] |
group | task属于哪个组 | null |
description | task的描述 | null |
带参任务
// 定义一个名字为paramTask的task,属于it235分组,并且依赖myTask1和myTask2两个task。
task myTask1{
}
task myTask2{
}
project.task('paramTask', group: "it235", description: "我自己的Task", dependsOn: ["myTask1", "myTask2"] ).doLast {
println "execute paramTask"
}
这些参数也可以写在task的闭包体内
task myTask1{
}
task myTask2{
}
task it235Task {
group "it235"
description "我自己的Task"
//任务依赖,先忽略接下来会讲
dependsOn myTask1, myTask2
doLast {
println "execute it235"
}
}
指定Type
Task创建的时候可以通过 type: SomeType 指定Type,Type其实就是告诉Gradle,这个新建的Task对象会从哪个基类Task派生。比如,Gradle本身提供了一些通用的Task,最常见的有Copy 任务。Copy是Gradle中的一个类。当我们:task myTask(type:Copy)的时候,创建的Task就是一个Copy Task。Gradle本身还提供了其他如:Delete、Sync等供我们使用。
实现了CopySpec接口,使用CopySpec.from()方法可以指定源文件,CopySpec.into()方法可以指定目标目录。
task myTask(type: Copy) { configure closure }
拷贝文件例子
task copyDocs(type: Copy) { from 'src/main/doc' into 'build/target/doc' } //这是个Ant filter import org.apache.tools.ant.filters.ReplaceTokens //这是一个闭包 def dataContent = copySpec { from 'src/data' include '*.data' } task initConfig(type: Copy) { from('src/main/config') { include '**/*.properties' include '**/*.xml' filter(ReplaceTokens, tokens: [version: '2.3.1']) } from('src/main/config') { exclude '**/*.properties', '**/*.xml' } from('src/main/languages') { rename 'EN_US_(.*)', '$1' } into 'build/target/config' exclude '**/*.bak' includeEmptyDirs = false with dataContent } task chVer(type: Copy) { from "src/main/manifest/AndroidManifestCopy.xml" // 复制src/main/manifest/目录下的AndroidManifest.xml into 'src/main' // 复制到指定目标目录 rename { String fileName -> //在复制时重命名文件 fileName = "AndroidManifest.xml" // 重命名 } }
创建ZIP归档文件,默认压缩文件类型为zip。
task zip(type: Zip) { from 'src/dist' into('libs') }
其他任务
Task Class Description Copy 将文件复制到目标目录 Delete 删除文件和目录 Exec 执行命令行程序 GradleBuild 执行Gradle构建 JavaExec 在一个子进程中执行一个Java程序 SourceTask 对源文件执行一些操作 Sync 同步文件或者目录 Upload 将配置的组件上传到执行的仓库 WorkResults Helps access trivial WorkResult objects. - 更多示例请戳
通过TaskAction指定一个任务
class GreetingTask extends DefaultTask {
@Input
String message = 'This is GreetingTask'
// @TaskAction 表示该Task要执行的动作,即在调用该Task时,hello()方法将被执行
@TaskAction
def hello(){
println "Hello world. $message"
}
}
// hello使用了默认的message值
task hello(type:GreetingTask)
//与register相同
tasks.register('hello', GreetingTask)
// 重新设置了message的值
task hello1(type:GreetingTask){
message ="I am an java developer"
}
任务依赖
task之间的依赖关系是通过task name
来决定的。我们可以在同一个项目中做task之间的依赖:
task hello {
doLast { println 'Hello www.it235.com!' }
}
task intro {
dependsOn hello
doLast { println "I'm 君哥" }
}
也可以跨项目进行task的依赖,如果是跨项目的task依赖的话,需要制定task的路径:
project('it235-service') {
task service{
dependsOn ':it235-dao:dao'
doLast { println 'service' }
}
}
project('it235-dao') {
task dao{
doLast { println 'dao' }
}
}
或者我们可以在定义好task之后,再处理task之间的依赖关系:
执行顺序
有时候我们的task之间是有执行顺序的,我们称之为对task的排序ordering。
先看一下ordering和dependency有什么区别。dependency表示的是一种强依赖关系,如果taskA依赖于taskB,那么执行taskA的时候一定要先执行taskB。
而ordering则是一种并不太强列的顺序关系。表示taskA需要在taskB之后执行,但是taskB不执行也可以。
在gradle中有两种order:分别是must run after和should run after。
taskA.mustRunAfter(taskB)表示必须遵守的顺序关系,而taskA.shouldRunAfter(taskB)则不是必须的,在下面两种情况下可以忽略这样的顺序关系: 第一种情况是如果shouldRunAfter引入了order循环的时候。
第二种情况是如果在并行执行的情况下,task所有的依赖关系都已经满足了,那么也会忽略这个顺序。
我们看下怎么使用:
task taskX {
doLast { println 'www.it235.com' }
}
task taskY {
doLast { println 'hello' }
}
taskY.mustRunAfter taskX
//taskY.shouldRunAfter taskX
条件执行
我们可以给task一些描述信息,这样我们在执行gradle tasks的时候,就可以查看到:
有时候我们需要根据build文件中的某些属性来判断是否执行特定的task,我们可以使用onlyIf :
task hello {
doLast { println 'www.it235.com' }
}
hello.onlyIf { !project.hasProperty('skipHello') }
或者我们可以抛出StopExecutionException异常,如果遇到这个异常,那么task后面的任务将不会被执行:
task eat {
doLast { println 'We're having dinner.' }
}
eat.doFirst {
if (true) {
throw new StopExecutionException()
}
}
task myTask {
dependsOn('eat')
doLast {
println 'I am not affected'
}
}
我们还可以启动和禁用task:
myTask.enabled = false
最后我们还可以让task超时,当超时的时候,执行task的线程将会被中断,并且task将会被标记为failed。
如果我们想继续执行,那么可以使用 --continue。
注意, 只有能够响应中断的task,timeout才有用。
import java.time.Duration
task hangingTask() {
doLast {
Thread.sleep(100000)
}
//设定的超时时间,需要导Duration包
timeout = Duration.ofMillis(500)
}
Gradle命令是支持连在一起拼写的,比如gradle hello hello1,那么gradle将会先执行hello,然后再执行hello1。
Gradle命令是可以缩写的,前提是这个缩写能够唯一地限定一个task。比如rygTask,那么执行的时候就可以缩写为gradle rTask。
task rule
如果我们想要给某些task定义一些规则,那么可以使用tasks.addRule:
tasks.addRule("Pattern: ping<ID>") {
String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) {
doLast { println "Pinging: " + (taskName - 'ping') }
}
}
}
上我们定义了一个rule,如果taskName是以ping开头的话,那么将会输出对应的内容。
运行结果:
> gradle -q pingServer1
Pinging: Server1
我还可以将这些rules作为依赖项引入:
task groupPing { dependsOn pingServer1, pingServer2 }
Finalizer tasks
和java中的finally一样,task也可以指定对应的finalize task:
task execute {
doLast {
println '执行程序'
}
}
task release {
doLast { println '释放资源' }
}
execute.finalizedBy release
> gradle -q execute
执行程序
释放资源
finalize task一定会被执行,即使上面的execute中抛出了异常,release也会被成功执行。
总结
本篇给大家讲解了task在gradle中的作用以及其内部原理,结合前面的project的文章,我们已经初步掌握了build.gradle的基本使用,接下来我们继续学习gradle中另外一个重要的概念:插件
关注君哥继续肝起来