gradle task

1. Task定义及配置

1.1. 定义

直接使用task函数进行定义:

task helloTask {
    println 'I am helloTask'
}

通过TaskContainer创建Task

this.tasks.create(name: 'helloTask2') {
    println 'I am helloTask2'
}

gradle是通过TaskContainer管理当前Project中所有的Task。

1.2. 配置

为task设置组名和描述信息:

task helloTask (group: 'imooc', description: 'task study') {
    println 'I am helloTask'
}

this.tasks.create(name: 'helloTask2') {
    setGroup('imooc')
    setDescription('task study')
    println 'I am helloTask2'
}

上述2中创建task的方法效果都是一样的,最终都会把创建的task添加到task TaskContainer中。
group分组后,gradle会把相同group的task放一起,方便管理,如果不添加分组,就会被gradle放到other分组。
description是普通描述信息,说明该task的作用

2. Task的执行详解

定义中的例子输出都是在task的配置阶段进行输出的,所以上述例子会输出task中的2个task中的打印语句。任何task的执行整个project中所有的配置代码都会被这行。使用task的doFirst和doLast会运行在gradle的执行周期,下面看看如何让task里面的代码在执行阶段运行。

task helloTask (group: 'imooc', description: 'task study') {
    println 'I am helloTask'
    doFirst {
        println 'the task group is: ' + group
    }
    // 可以多次调用
    doFirst {}
}

helloTask.doFirst {
    println 'the task description is: '+ description
}

在task内部可以多次调用doFirst,也可以在task外部进行调用doFirst,执行时先执行外部的doFirst,再执行task内部定义的。

E:\studio\gradletest>gradlew helloTask
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Configure project :app
I am helloTask

> Task :app:helloTask
the task description is: task study
the task group is: imooc

doFirst和doLast的区别,gradle已经为我们默认实现了许多task,可以通过doFirst可以为已经有的task之前添加相应的逻辑,通过doLast可以为已经有的task之后添加相应的逻辑。
下面实现一个计算build执行时长的功能:

// 在这个方法执行的时候,project所有的task都已经被有序地创建好了
// 如果直接在脚本找,有些task没有被创建完毕,会出现task找不到报错
this.afterEvaluate {
    def preBuildTask = project.tasks.getByName('preBuild')
    preBuildTask.doFirst {
        startBuildTime = System.currentTimeMillis()
        println 'the start time is: ' + startBuildTime
    }
    def buildTask = project.tasks.getByName('build')
    buildTask.doLast {
        endBuildTime = System.currentTimeMillis()
        println "the build time is: ${endBuildTime - startBuildTime}"
    }
}

3. Task的依赖和执行顺序


决定task执行顺序主要有3中方式: 
1.    dependsOn强依赖方式
2.    通过Task输入输出指定
3.    通过API指定实行顺序
其中方式1和方式2是等效的。
task依赖示例:

/**
 * task 依赖
 */
task taskX {
    doLast {
        println 'taskX'
    }
}

task taskY {
    doLast {
        println 'taskY'
    }
}
task taskZ(dependsOn : [taskX, taskY]) {
    doLast {
        println 'taskZ'
    }
}

使用dependsOn指定依赖,如果多个依赖,使用列表进行定义,输出

E:\studio\gradletest>gradlew taskZ

> Task :app:taskX
taskX

> Task :app:taskY
taskY

> Task :app:taskZ
taskZ

也可以使用下面方式指定依赖:

taskZ.dependsOn(taskX, taskY)

被依赖的task如果没有依赖关系,他们的执行的时机是随机的。
使用<<的作用完全等于doLast方法。
task动态依赖:

task lib1 << {
    println 'lib1'
}

task lib2 << {
    println 'lib2'
}

task noLib << {
    println 'noLib'
}

task taskZ {
    dependsOn this.tasks.findAll { task ->
        println 'dependsOn: ' + task.name
        task.name.startsWith('lib')
    }
    doLast {
        println 'taskZ'
    }
}

taskZ需要放到依赖task的后面,如果放在前面,在执行dependsOn会发现tasks中没有在taskZ后面task,导致依赖失败。
实际运用,解析xml并输出到对应的文件:
xml的内容:

<releases>
  <release>
    <versionCode>100</versionCode>
    <versionName>1.0.0</versionName>
    <versionInfo>App的第1个版本,上线了一些最基础核心的功能.</versionInfo>
  </release>
  
  
  <release>
    <versionCode>110</versionCode>
    <versionName>1.1.0</versionName>
    <versionInfo>App的第2个版本,上线了一些最基础核心的功能.</versionInfo>
  </release>
  
  
  <release>
    <versionCode>1</versionCode>
    <versionName>1.2.0</versionName>
    <versionInfo>第8个版本。。。</versionInfo>
  </release>
</releases>

build.gradle

task handleReleaseFile {
    def srcFile = file('releases.xml')
    def destDir = new File(this.buildDir, 'generated/release/')
    doLast {
        println '开始解析对应的xml文件...'
        destDir.mkdir()
        def releases = new XmlParser().parse(srcFile)
        releases.release.each{releaseNode ->
            // 解析每个release节点的内容
            def name = releaseNode.versionName.text()
            def versionCode = releaseNode.versionCode.text()
            def versionInfo = releaseNode.versionInfo.text()
            // 创建文件,并写入节点数据
            def destFile = new File(destDir, "release-${name}.txt")
            destFile.withWriter { writer ->
                writer.write("${name} -> ${versionCode} -> ${versionInfo}")

            }
        }
    }
}

task handleReleaseFileTest (dependsOn : handleReleaseFile){
    def dir = fileTree( 'build/generated/release/')
    doLast {
        dir.each {
            println 'the file name is: ' + it
        }
        println '输出完成...'
    }
}

在控制台输入命令运行task:

gradlew handleReleaseFileTest

输出结果:

> Task :app:handleReleaseFile
开始解析对应的xml文件...

> Task :app:handleReleaseFileTest
the file name is: E:\studio\gradletest\app\build\generated\release\release-1.0.0.txt
the file name is: E:\studio\gradletest\app\build\generated\release\release-1.1.0.txt
the file name is: E:\studio\gradletest\app\build\generated\release\release-1.2.0.txt
输出完成...

4.    Task输入输出
先看一张图:
 
上图Task One和Task Two分别代表2个Task,inputs和outputs是Task的2个属性,Task One的输出是Task Two的输入。
TaskInputs代表Task的输入类,TaskInputs接收的输入参数类型包括:files,file,dir和key-value类型的属性,value可以是任何对象类型。也就是说Task输入可以是任意对象,文件,多个文件和文件夹。
TaskOutputs表示Task的输出类,输出类型包括:files,file,dir,也就是说TaskOutputs的输出是文件类型。
gradle规定,如果一个task同时依赖于另外两个task,并且另外两个task的其中一个task输出作为另外一个task的输入,那么会先执行生产者的task。
下面的代码功能:根据版本信息生成release.xml文件

/**
 * task的输入输出详解
 */
import groovy.xml.MarkupBuilder

ext {
    versionName = '1.1.0'
    versionCode = '110'
    versionInfo = 'App的第2个版本,上线了一些基础的核心功能'
    destFile = file('releases.xml')
    if(destFile != null && !destFile.exists()) {
        destFile.createNewFile()
    }
}

task readTask {
    inputs.file destFile
    doLast {
        def file = inputs.files.singleFile
        println file.text
    }
}

task writeTask {
    // 为Task指定输入
    inputs.property('versionCode', this.versionCode)
    inputs.property('versionName', this.versionName)
    inputs.property('versionInfo', this.versionInfo)

    outputs.file destFile

    doLast {
        def data = inputs.getProperties()
        File file = outputs.getFiles().getSingleFile()

        // 将Map转化为实体对象
        def versionMsg = new VersionMsg(data)
        def sw = new StringWriter()
        def xmlBuilder = new MarkupBuilder(sw)
        if(file.text != null && file.text.size() <= 0) {
            // 文件中没有内容
            xmlBuilder.releases {
                release {
                    versionCode(versionMsg.versionCode)
                    versionName(versionMsg.versionName)
                    versionInfo(versionMsg.versionInfo)
                }
            }
            file.withWriter{writer -> writer.append(sw.toString())}
        } else {
            // 已有版本信息
            xmlBuilder.release {
                versionCode(versionMsg.versionCode)
                versionName(versionMsg.versionName)
                versionInfo(versionMsg.versionInfo)
            }
            // 将生成的xml数据插入到根节点之前
            def lines = file.readLines()
            def lengths = lines.size() - 1
            file.withWriter { writer ->
                lines.eachWithIndex { String line, int index ->
                    if(index != lengths) {
                        writer.append(line + '\r\n')
                    } else if(index == lengths) {
                        writer.append('\r\r\n' + sw.toString() + '\r\n')
                        writer.append(lines.get(lengths))
                    }

                }

            }

        }
    }
}

class VersionMsg {
    String versionCode
    String versionName
    String versionInfo
}

task taskTest {
    dependsOn readTask, writeTask
    doLast {
        println '输入输出任务结束'
    }
}

5. 挂接到构建生命周期

5.1. task执行顺序

task执行顺序的指定除了使用dependsOn强依赖方式和通过Task输入输出指定,还可以通过API指定实行顺序。
mustRunAfter它可以指定一个,也可以指定多个,用法完全一样。

/**
 * 执行顺序的指定
 */
task taskX {
    doLast {
        println 'taskX'
    }
}

task taskY {
    mustRunAfter taskX
    doLast {
        println 'taskY'
    }
}

task taskZ {
    mustRunAfter taskY
    doLast {
        println 'taskZ'
    }
}

控制台输入顺序错乱的task:

gradlew taskY taskZ taskX

输出:

E:\studio\gradletest>gradlew taskY taskZ taskX

> Task :app:taskX
taskX

> Task :app:taskY
taskY

> Task :app:taskZ
taskZ

shouldRunAfter和mustRunAfter用法完全一样,唯一区别是mustRunAfter是强制task必须在另外的task之后执行,而shouldRunAfter是非强制性的。

5.2. 挂接到构建生命周期

this.project.afterEvaluate { project ->
    def buildTask = project.tasks.getByName('build')
    if(buildTask == null) {
        throw GradleException('the build task is not found')
    }
    buildTask.doLast {
        writeTask.execute()
    }
}

5.3. 挂接到构建生命周期的中间

指定运行在某些task之后,某个task之前。

6. task类型

参考官方文档:https://docs.gradle.org/current/dsl/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值