Gradle入门系列(四)——初识Gradle Task

Gradle中的Task

一、Task定义及配置

TaskContainer:管理所有的Task,如:增加、查找。

定义(创建)Task

// 直接通过task函数去创建
task helloTask {
  println 'i am helloTask.'
}

// 通过TaskContainer去创建
this.tasks.create(name: 'helloTask2') {
  println 'i am helloTask 2.'
}

配置Task

// 给Task指定分组与描述
task helloTask(group: 'study', description: 'task study'){ // 语法糖
  ...
}
task helloTask {
  group 'study' // setGroup('study')
  description 'task study' // setDescription('task study')
  ...
}

Task除了可以配置group、description外,还可以配置name、type、dependsOn、overwrite、action。

结论:

  • 给Task分组之后,该task会被放到指定组中,方便归类查找。(默认被分组到other中)
  • 给Task添加描述,相当于给方法添加注释。

二、Task的执行详情

Task中doFirst与doLast的使用:

task helloTask {
  println 'i am helloTask.'
  doFirst {
    println 'the task group is: ' + group
  }
  // doFirst、doLast可以定义多个
  doFirst {}
}
// 外部指定doFirst(会比在闭包内部指定的doFirst先执行)
helloTask.doFirst {
  println 'the task description is: ' + description
}

// 统计build执行时长
def startBuildTime, endBuildTime
this.afterEvaluate { Project project ->
  // 保证要找的task已经配置完毕
  def preBuildTask = project.tasks.getByName('preBuild') // 执行build任务时,第一个被执行的Task
  preBuildTask.doFirst {
    startBuildTime = System.currentTimeMillis()
  }
  def buildTask = project.tasks.getByName('build') // 执行build任务时,最后一个被执行的Task
  buildTask.doLast {
    endBuildTime = System.currentTimeMillis()
    println "the build time is: ${endBuildTime - startBuildTime}"
  }
}

结论:

  • Task闭包中直接编写的代码,会在配置阶段执行。可以通过doFirst、doLast块将代码逻辑放到执行阶段中执行。
  • doFirst、doLast可以指定多个。
  • 外部指定的doFirst、doLast会比内部指定的先执行。
  • doFirst、doLast可以对gradle中提供的已有的task进行扩展。

三、Task的执行顺序

task执行顺序指定的三种方式:

  1. dependsOn强依赖方式
  2. 通过Task输入输出指定(与第1种等效)
  3. 通过API指定执行顺序

1、Task的依赖

// ============= dependsOn强依赖方式 =============
task taskX {
  doLast {
      println 'taskX'
  }
}
task taskY {
  doLast {
      println 'taskY'
  }
}
// 方式一:静态依赖
// task taskZ(dependsOn: taskY) // 依赖一个task
task taskZ(dependsOn: [taskX, taskY]) { // 依赖多个task,需要用数组[]表示
  doLast {
      println 'taskZ'
  }
}
// 方式二:静态依赖
taskZ.dependsOn(taskX, taskY)
// 方式三:动态依赖
task taskZ() {
  dependsOn this.tasks.findAll {    // 依赖所有以lib开头的task
    task -> return task.name.startsWith('lib')
  }
  doLast {
      println 'taskZ'
  }
}

其他:

  • taskZ依赖了taskX与taskY,所以在执行taskZ时,会先执行taskX、taskY。
  • taskZ依赖了taskX与taskY,但taskX与taskY没有关系,它们的执行顺序是随机的。

2、Task的输入输出

inputs和outputs是Task的属性。
inputs可以是任意数据类型对象,而outputs只能是文件(或文件夹)。
TaskA的outputs可以作为TaskB的inputs。

例子:writeTask输入扩展属性,输出文件,readTask输入writeTask的输出文件

ext {
    versionCode = '1.0.0'
    versionName = '100'
    versionInfo = 'App的第1个版本,完成聊天功能'
    destFile = file('release.xml')
    if (destFile != null && !destFile.exists()) {
        destFile.createNewFile()
    }
}

task writeTask {
    inputs.property('versionCode', this.versionCode)
    inputs.property('versionName', this.versionName)
    inputs.property('versionInfo', this.versionInfo)
    outputs.file this.destFile
    doLast {
        def data = inputs.getProperties() // 返回一个map
        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将xml数据写入到sw中
            xmlBuilder.releases { // <releases>
                release { // <releases>的子节点<release>
                    versionCode(versionMsg.versionCode)
                    // <release>的子节点<versionCode>1.0.0<versionCode>
                    versionName(versionMsg.versionName)
                    versionInfo(versionMsg.versionInfo)
                }
            }
            // 将sw里的内容写到文件中
            file.withWriter { writer ->
                writer.append(sw.toString())
            }
        } else { // 已经有其它版本信息了
            xmlBuilder.release {
                versionCode(versionMsg.versionCode)
                versionName(versionMsg.versionName)
                versionInfo(versionMsg.versionInfo)
            }
            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(sw.toString() + '\r\n')
                        writer.append(line + '\r\n')
                    }
                }
            }
        }
    }
}

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

task taskTest(dependsOn: [writeTask, readTask]) {
    doLast {
        println '任务执行完毕'
    }
}

class VersionMsg {
    String versionCode
    String versionName
    String versionInfo
}

通过执行 gradle taskTask 之后,就可以在工程目录下看到release.xml文件了。

结论:

  • 因为writeTask与readTask通过inputs、outputs产生了关联关系,所以,readTask一定会在writeTask执行之后才执行。

3、Task API指定顺序

task指定执行顺序的api有:

  • mustRunAfter : 强行指定在某个或某些task执行之后才执行。
  • shouldRunAfter : 与mustRunAfter一样,但不强制。
task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    // shouldRunAfter taskX
    mustRunAfter taskX
    doLast {
        println 'taskY'
    }
}
task taskZ {
    mustRunAfter taskY
    doLast {
        println 'taskZ'
    }
}

通过执行 gradle taskY taskZ taskX 之后,可以看到终端还是按taskX、taskY、taskZ顺序执行的。

四、挂接到构建生命周期

例子:build任务执行完成后,执行一个自定义task

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

例子:Tinker将自定义的manifestTask插入到了gradle脚本中processManifest与processResources这两个任务之间

TinkerManifestTask manifestTask = project.tasks.create("tinkerProcess${variantName}Manifest", TinkerManifestTask)
...
manifestTask.mustRunAfter variantOutput.processManifest
variantOutput.processResources.dependsOn manifestTask

五、Task类型

Gradle DSL Version 5.1

Copy - Gradle DSL Version 5.1

欢迎关注微信公众号:全栈行动
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Gradle的一些命令记录: 执行一个指定的任务:gradle -q taskName // 如:g...
    zhaoyubetter阅读 637评论 0 0
  • 介绍 到目前为止,我们已经看到了很多Gradle构建的属性,并且知道了怎么去执行Tasks。这一章,会更多的了解这...
    None_Ling阅读 1,556评论 0 0
  • gradle脚本中只有task可以让配置脚本能在执行阶段执行,其他都是在配置阶段执行. task定义及配置: //...
    高斯巴阅读 254评论 0 0
  • Gradle 是一款构建系统工具,它的 DSL 基于 Groovy 实现。Gradle 构建的大部分功能都是通过插...
    任教主来也阅读 2,952评论 3 6
  • 把意识形态作为一种行为手段或一种工具使用的人们,在其使用过程中,陷进了意识形态之中并被它所包围,而人们还自以为是意...
    凌飒阅读 109评论 0 0