博客主页java
接下来说解Gradle核心Task——任务。主要内容有如何多种方式建立任务,如何访问任务的方法和属性,任务执行过程和实战,任务执行顺序和实战, 任务依赖和实战,任务的输入输出,若是挂载自定义Task到构建过程当中,如何对任务进行分组、排序,以及一些规则性的知识。segmentfault
可使用Project提供的task方法或者经过TaskContainer提供的create方法。网络
经过Project中的task(String name)方法建立任务闭包
def customTask0 = task('customTask0') // 调用任务的doLast 方法,该方法在任务执行阶段执行。 customTask0.doLast { println "建立任务方法原型: Task task(String name)" }
customTask0就是建立的任务名字,经过gradlew customTask0执行这个任务。app
def customTask1 = task(group: 'help', 'customTask1') customTask1.doLast { println "建立任务方法原型: Task task(Map<String, ?> args, String name)" println "任务分组: ${customTask1.group}, 任务名字:${customTask1.name}" // 任务分组: build, 任务名字:customTask1 }
其中Map参数用来对建立的Task进行配置,上例中指定任务的分组为help,该任务就会分组到help组中。单元测试
// 方式一:建立任务并配置任务 task customTask2 { // 配置任务的分组 group 'myTask' // 配置任务的描述信息 description '任务名+闭包方式建立任务' // 处理任务执行后须要作的事 doLast { println "建立方法原型:Task task(String name, Closure configureClosure)" println "任务分组:${customTask2.group}, 任务描述:${customTask2.description}" } } // 方式二:建立任务并配置任务 task customTask2(group: 'myTask', description: '任务名+闭包方式建立任务') { doLast { println "建立方法原型:Task task(String name, Closure configureClosure)" println "任务分组:${customTask2.group}, 任务描述:${customTask2.description}" } }
除了可使用Map参数配置任务,还可使用闭包的方式对任务进行配置。由于闭包中的委托对象就是Task,全部可使用Task对象的任何方法、属性进行配置。测试
查看Task源码可知,可用的配置以下:
配置项的详细讲解:gradle
// 用于配置任务的描述,默认值:null String TASK_DESCRIPTION = "description"; // 用于配置任务的分组,默认值:null String TASK_GROUP = "group"; // 基于一个存在的Task来建立,和咱们类继承差很少,默认值:DefaultTask String TASK_TYPE = "type"; // 用于配置任务的依赖,默认值:[] String TASK_DEPENDS_ON = "dependsOn"; // 是否替换存在的Task,这个和type配合起来用,默认值:false String TASK_OVERWRITE = "overwrite"; // 添加到任务中的一个Action或者一个闭包,默认值:null String TASK_ACTION = "action";
tasks.create('customTask3') { group 'myTask' description '经过TaskContainer建立任务' doLast { println "TaskContainer的create建立任务原型:Task create(String name, Closure configureClosure)" println "任务分组: ${group}, 任务描述: ${description}" } }
tasks是Project对象的属性,类型是TaskContainer,能够直接调用它的create方法建立任务。ui
咱们建立的任务都会做为Project的一个属性,属性名就是任务名,因此能够直接经过任务名访问该任务。this
def customTask0 = task('customTask0') // 经过任务名访问 customTask0.doLast { println "建立任务方法原型: Task task(String name)" }
其实TaskContainer就是咱们建立任务的集合,在Project中能够经过tasks属性访问TaskContainer,能够经过访问集合的方式访问任务。
tasks['customTask3'].doFirst { println "经过访问集合的方式访问任务." }
经过任务名获取任务,其实调用的就是tasks.getAt('customTask3'),最后调用的是findByName(String name)实现。
get访问方式若是找不到任务,就会抛出UnknownTaskException异常。
find访问方式若是找不到任务,就会返回null
task customTask4 tasks['customTask4'].doLast { // find方式访问 println "经过路径find方式访问: ${tasks.findByPath(':customTask4')}" println "经过名称find方式访问: ${tasks.findByName('customTask4')}" // get方式访问 println "经过路径get方式访问: ${tasks.getByPath(':customTask4')}" println "经过名称get方式访问: ${tasks.getByName('customTask4')}" }
统计执行阶段的时间,也就是全部Task的执行的全部时间。
def startBuildTime, endBuildTime // afterEvaluate配置阶段完成调用 this.afterEvaluate { Project project -> println "-----------配置阶段完成--------------" // 全部Task配置完成后,找到第一个执行的Task def preBuildTask = project.tasks.findByName('preBuild') if (preBuildTask) { preBuildTask.doFirst { startBuildTime = System.currentTimeMillis() println "task build start time: ${startBuildTime}" } } def buildTask = project.tasks.findByName('build') if (buildTask) { buildTask.doLast { endBuildTime = System.currentTimeMillis() println "the build cost time: ${endBuildTime - startBuildTime}" } } }
上一篇中已经讲解了任务依赖,单个任务和多个任务依赖,能够经过dependsOn指定其依赖的任务。可是咱们也能够经过匹配指定依赖的任务。
task myTask1 { doLast { println "myTask1>>doLast" } } task myTask2 { doLast { println "myTask2>>doLast" } } task customTask5 { // 经过匹配,查看依赖任务 dependsOn this.project.tasks.findAll { Task task -> println "task name>>> ${task.name}" return task.name.startsWith('myTask') } doLast { println "customTask5>>doLast" } }
将发布版本文档的输出到每一个版本单独文档中实战。
// releases.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> </releases>
将解析文档后的内容写入到${buildDir}/generated/release/release-${versionCode}.txt
文件中
tasks.create('handleReleaseInfoTask') { println "buildDir>>> ${this.buildDir.path}" def srcFile = file('releases.xml') def destDir = new File(this.buildDir, 'generated/release/') doLast { println "开始解析releases.xml文件" if (!destDir.isDirectory()) destDir.mkdirs() def releases = new XmlParser().parse(srcFile) releases.release.each { Node releaseNode -> def versionCode = releaseNode.versionCode.text() def versionName = releaseNode.versionName.text() def versionInfo = releaseNode.versionInfo.text() // 建立文件写入 def descFile = new File(destDir, "release-${versionCode}.txt") descFile.withWriter { writer -> writer.write("${versionCode}->${versionName}->${versionInfo}") } } } } // 测试任务handleReleaseInfoTaskTest依赖handleReleaseInfoTask任务 task handleReleaseInfoTaskTest(dependsOn: handleReleaseInfoTask) { def fileDir = fileTree("${this.buildDir.path}/generated/release/") doLast { fileDir.each { println "the file name>>> ${it}" } println "解析完成." } }
任务是能够分组和添加描述的,分组就是对任务分类。在经过执行gradlew tasks查看任务信息时,就能够看到不一样组下的任务,并还能够看到任务描述信息。
// 配置任务的分组和描述信息 task customTask6(group: 'myTask', description: '任务分组和描述案例') { doLast { println "group: ${group}, description: ${description}" } }
添加分组后,能够在组里找到相应的任务,以下图所示:
<< 操做符是Gradle的Task中的doLast方法的短标记形式,也就是<<代替doLast方法。
task customTask7 << { println "customTask7 doLast" }
其实<<操做符在Groovy中能够重载的,查看源码可知,在Task接口中对应leftShift方法重载了<<操做符。
当执行一个Task时,其实就是执行Task对象中的actions列表,其类型是一个List
task customTask8(type: CustomTask) { doFirst { println "Task执行以前执行:doFirst" } doLast { println "Task执行以后执行:doLast" } } class CustomTask extends DefaultTask { @TaskAction def doSelf() { println "Task本身自己在执行:doSelf" } } > gradlew customTask8 // 执行Task后输出的日志信息 Task执行以前执行:doFirst Task本身自己在执行:doSelf Task执行以后执行:doLast
上例中定义一个Task类型CustomTask , 被TaskAction注解标记的方法,表明Task自己执行要执行的方法。
其实doFirst ,doSelf,doLast 这个三个方法可以按照顺序执行,那么在actions列表中必须按照顺序排列的。
在Task建立时,Gradle就会解析被TaskAction标记的方法做为其Task执行的Action,经过actions.add(0, action)添加 到actions列表中。
doFirst方法经过actions.add(0, action)添加到actions列表中,doFirst就会出如今doSelf前面;而doLast经过actions.add(action)添加到actions列表中,doLast就会出如今doSelf后面。因此在执行Task的时,就达到顺序执行的目的。
能够经过 mustRunAfter 和 shouldRunAfter 方法控制一个任务必须或者应该在某个任务后执行。
taskB.shouldRunAfter(taskA) 表示taskB应该在taskA执行后执行,可能任务顺序不会按照指望的执行。
taskB.mustRunAfter(taskA) 表示taskB必须在taskA执行后执行。
task customTask10 { doLast { println "TasK: customTask10" } } task customTask9 { mustRunAfter customTask10 doLast { println "TasK: customTask9" } } > gradlew customTask9 customTask10 // 执行后输出日志信息 TasK: customTask10 TasK: customTask9
Task有个enabled属性能够启动和禁用任务。默认为true,表示启动;当设置为false,输出会提示该任务被Skipping。
task customTask11 { doLast { println "TasK: customTask11" } } customTask11.enabled = false > gradlew -i customTask11 // 输出的日志信息 Skipping task ':customTask11' as task onlyIf is false.
断言就是一个条件表达式。Task中有一个onlyIf方法,接受闭包做为参数,当该闭包返回true,该任务执行,不然跳过。
应用场景:能够控制程序哪些状况下打什么包,何时执行单元测试,什么状况下执行单元测试时候不执行网络测试。
案例实战:假设打渠道包时,若是直接build会编译出全部的包,太慢!能够经过onlyIf控制
tasks.create('buildHuaweiRelease') { doLast { println "build 华为渠道包." } onlyIf { def execution = true if (project.hasProperty('build_apps')) { Object build_apps = project.property('build_apps') println "buildHuaweiRelease>>> build_apps: ${build_apps}" if ('all'.equals(build_apps) || 'shoufa'.equals(build_apps)) { execution = true } else { execution = false } } return execution } } task buildMIUIRelease { doLast { println "build MIUI渠道包." } onlyIf { def execution = true if (project.hasProperty('build_apps')) { Object build_apps = project.property('build_apps') println "buildMIUIRelease>>> build_apps: ${build_apps}" if ('all'.equals(build_apps) || 'shoufa'.equals(build_apps)) { execution = true } else { execution = false } } return execution } } task buildQQRelease { doLast { println "build QQ渠道包." } onlyIf { def execution = true if (project.hasProperty('build_apps')) { Object build_apps = project.property('build_apps') println "buildMIUIRelease>>> build_apps: ${build_apps}" if ('all'.equals(build_apps) || 'exclude_shoufa'.equals(build_apps)) { execution = true } else { execution = false } } return execution } } task buildTask { group BasePlugin.BUILD_GROUP description '打渠道包' dependsOn buildHuaweiRelease, buildMIUIRelease, buildQQRelease }
上例中buildHuaweiRelease 和 buildMIUIRelease 是首发渠道包,buildQQRelease 不是首发渠道包,能够经过build_apps属性控制打哪些渠道包
// 打全部渠道包 gradlew buildTask gradlew -Pbuild_apps=all buildTask // 打首发渠道包 gradlew -Pbuild_apps=shoufa buildTask // 打非首发渠道包 gradlew -Pbuild_apps=exclude_shoufa buildTask
命令行中-P意思是:为Project指定K-V格式的属性键值对,格式为:-PK=V
当执行或者依赖的任务不存在时,添加任务规则后,能够对执行失败的任务作一些操做。
// 任务名做为闭包的参数 tasks.addRule('规则描述') {String taskName -> task(taskName) { doLast { println "${taskName}任务不存在" } } } task ruleTaskTest { dependsOn missTask } // 执行后属性日志信息 missTask任务不存在
Task提供了inputs 和 outputs 输入输出属性。
Task输入输出案例实战:版本发布文档自动维护
步骤:请求本次发布的版本相关信息->将版本相关信息解析出来->将解析出来的数据生成xml格式数据->写入已有的文档数据中
请求版本信息这一步使用自定义属性方式代替,首先定义版本相关信息以下
ext { versionCode = 105 versionName = '1.0.5' versionInfo = 'App first version.' destVersionOutputsFile = this.project.file('releases.xml') if (!destVersionOutputsFile.exists()) { destVersionOutputsFile.createNewFile() } } // 用于封装版本信息 class Version { def versionCode def versionName def versionInfo }
建立一个写入任务writeVersionTask
tasks.create('writeVersionTask') { group 'myTask' description '版本信息自动写入任务.' inputs.property('versionCode', versionCode) inputs.property('versionName', versionName) inputs.property('versionInfo', versionInfo) outputs.file destVersionOutputsFile doLast { println "版本信息自动写入任务开始." def versionData = inputs.getProperties() def version = new Version(versionData) def writerFile = outputs.files.singleFile def sw = new StringWriter() def markupBuilder = new MarkupBuilder(sw) if (writerFile.text != null && writerFile.text.size() <= 0) { // 第一次写入 markupBuilder.releases { markupBuilder.release { versionCode(version.versionCode) versionName(version.versionName) versionInfo(version.versionInfo) } } writerFile.withWriter { Writer writer -> writer.write(sw.toString()) } } else { // 已有其余版本信息 markupBuilder.release { versionCode(version.versionCode) versionName(version.versionName) versionInfo(version.versionInfo) } def lines = writerFile.readLines() def linesSize = lines.size() writerFile.withWriter { Writer writer -> lines.eachWithIndex { line, index -> println "line: ${line}, index: ${index}" if (index != linesSize - 1) { writer.append(line).append('\n') } else { // 最后一行 writer.append(sw.toString()).append('\n').append('\n') writer.append(line) } } } } println "版本信息自动写入任务结束." } }
建立一个读取任务readVersionTask
tasks.create('readVersionTask') { group 'myTask' description '版本信息自动读取任务.' mustRunAfter writeVersionTask inputs.file destVersionOutputsFile doLast { def readFile = inputs.files.singleFile println readFile.text } }
建立一个测试任务versionTaskTest
tasks.create('versionTaskTest') { dependsOn writeVersionTask, readVersionTask doLast { println "版本信息自动维护结束" } }
上例中,每次发布版本,都要手动执行writeVersionTask任务,怎么挂载在build构建过程当中呢?
// afterEvaluate:配置阶段完成调用,此时全部的Task解析完成 this.afterEvaluate { // 找到build任务 def buildTask = project.tasks.findByName('build') if (buildTask != null) { buildTask.doLast { // build任务执行完后调writeVersionTask任务 writeVersionTask.execute() } } }
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)