回顾
还记得上节内容中的例子吗?
task helloTask(group:'personal',description:'task learn'){
println 'this is hello task.'
}
this.tasks.create(name:'task1'){
setGroup('personal')
setDescription('task learn')
println 'this is hello task1.'
}
不知道你在执行 ./gradlew helloTask 的命令的时候有没有注意到,在控制台中输入了如下语句:
this is hello task.
this is hello task1.
Q:很明显在控制台中将task1的任务也输出了,这是为什么呢?
A:其实很简单,因为这两个输出语句都是在task的配置阶段去打印的,而在project中任何的task都是会执行的,所以这两个task就都会打印出来。
执行阶段
有时候我们并不需要在配置阶段就执行我们加载好的代码,而是在执行阶段才需要执行该怎么办呢?
我们可以为task在执行阶段中指定要执行的代码,而且也只有task能在执行阶段可以执行,task是gradle中的重要的核心部分,它占据gradle的一个生命周期。
如果我们想要在执行阶段来执行task,那个就需要借助task中的
doFirst()
doLast()
这两个方法,这两个使用方式相同,并且可以有多个存在,执行顺序将会按照我们编写的代码顺序执行
ttask helloTask(group:'personal',description:'task learn'){
println 'this is hello task.'
doFirst{
println 'this task group is:'+group
}
doFirst{
println 'this is doFirst2'
}
}
也可以在外部调用
helloTask.doFirst{
println 'this is doFirst3'
}
这两者加载输出有什么区别呢?
执行
./gradlew helloTask
输出
>>> helloTask
>>> this is doFirst3
>>> println 'this task group is:personal
从输出的语句中我们看到:
1. 首先输出了这个task的名称
2. 然后输出了我们在外部调用的doFirst()方法
3. 最后才输出了闭包中的doFirst()方法
也就是说如果我们想要task在执行阶段执行的话,必须要添加doFirst或者是doLast的闭包代码
那么我们就使用doFirst和doLast来实现一个功能吧
案例:计算build执行时长的gradle代码
def startTime,endTime
//监听执行阶段的完成(该回调可以确保所有的task都已经配置完成)
this.afterEvaluate{Project project->
//寻找最开始的task
def preBuildTask=project.tasks.getByName('preBuild')
preBuildTask.doFirst{
startTime=System.currentTimeMills()
println 'the start time is :'+startTime
}
//寻找build task
def buildTask=project.tasks.getByName('build')
preBuildTask.doLast{
endTime=System.currentTimeMills()
println 'the endTime time is :${endTime-startTime}'
}
}
或许有人会有疑惑,你怎么知道哪个task先执行?哪个最后执行呢?
这个就需要我们平时观察控制台输出中的内容了,在我们执行完 ./gradlew build操作后,控制台就会输出一大堆的内容,我们就能看到输出如下信息
app:preBuild
...
...
...
app:build
而这两个就是我们需要的task名称,通过观察控制台就能得到我们想要的信息
执行顺序
Taks的执行顺序由下面的三种方式来决定的
- dependsOn强依赖方式
task taskX{
doLast{
println 'taskX'
}
}
task taskY{
doLast{
println 'taskY'
}
}
task taskZ{
doLast{
println 'taskZ'
}
}
如果我们要为taskZ添加已知依赖,那么我们就需要使用dependsOn
//单个
task taskZ(dependsOn:taskX){
doLast{
println 'taskZ'
}
}
//多个
task taskZ(dependsOn:[taskX,taskY]){
doLast{
println 'taskZ'
}
}
等价于
taskZ.dependsOn(taskX,taskY)
在为Task添加依赖了后,那个我们输出的不仅仅是taskZ的内容了
我们可以通过将dot转换成下图,显示为:

如果想动态添加依赖,就需要dependsOn闭包方式添加依赖
// << 等价于doLast
task lib1 << {
println 'lib1'
}
task lib2 << {
println 'lib2'
}
task noLib << {
println 'noLib'
}
task taskZ{
dependsOn this.task.findAll{
//依赖以lib开头的依赖
task->return task.name.startsWith('lib')
}
doLast{
println 'taskZ'
}
}
案例:版本升级管理
versionfile.xml
<?xml version="1.0" encoding="utf-8">
<releases>
<release>
<versionCode>100<versionCode>
<versionName>1.0.0<versionName>
<versionInfo>新功能上线了<versionInfo>
<release>
<release>
<versionCode>101<versionCode>
<versionName>1.0.1<versionName>
<versionInfo>新功能上线了111<versionInfo>
<release>
<releases>
</xml>
gradle脚本实现版本升级管理,首先我们解析xml,然后拿到每个节点的信息
task versionControlFile {
def srcFile=file("versionfile.xml")
def destDir=new File(this.buildDir,'generated/release/')
doLast{
println '开始解析versionfile.xml文件'
destDir.mkdir()
//解析xml文件 根节点releases
def releases=new XmlParse().parse(srcFile)
//解析release并且遍历
releases.release.each{releaseNode->
def code=releaseNode.versionCode.text()
def name=releaseNode.versionName.text()
def info=releaseNode.versionInfo.text()
//创建文件并写入节点数据
def destFile=new File(destDir,'release-${name}.text')
destFile.withWrite{
writer->writer.wirte("${name}-> ${code} -> ${info}")
}
}
}
}
下面我们可以通过编写一个测试代码测试脚本是否能正确执行
task versionTest(dependsOn:versionControlFile){
def dir=fileTree(this.buildDir.path+'generated/release/')
doLast{
dir.each{
println 'the file name is '+it
}
}
println '完成输出......'
}
- 通过task指定输入输出方式
首先了解常见的方法
TaskInputs.java
TaskInputs files(Objects... path)
TaskInputs file(Object path)
TaskInputs dir(Object dirPath)
TaskInputs property(String name,Object value)
TaskInptus property(Map<String,?> properties)
TaskOutputs.java
TaskOutputs files(Objects... path)
TaskOutputs file(Object path)
TaskOutputs dir(Object dirPath)
使用指定输入输出方式改造一下gradle脚本实现版本升级管理的代码
ext{
versionName='1.0.0'
versionCode='100'
versionInfo='新版本上线'
destFile=file('versioncotrol.xml')
if(destFile!=null&&destFile.exists()){
destFile.createNewFile()
}
}
task wirteTask{
//指定输入
inputs.property('versionCode',this.versionCode)
inputs.property('versionName',this.versionName)
inputs.property('versionInfo',this.versionInfo)
//指定输出
outputs.file destFile
doLast{
//获取数据
def data=inputs.getProperties()
File outfile=outputs.getFiles().getSingleFile()
//将map类型的data数据转换成实体对象
def versionMsg=new VersionMsg(data)
//将实体对象转换成xml格式
def sw=new StringWriter()
def xmlBuild=new MakeUpBuilder(sw)
if(outfile.text!=null && outfile.text.size<=0){
//文件中没有内容
xmlBuild.releases{
release{
versionCode(versionMsg.versionCode)
versionName(versionMsg.versionName)
versionInfo(versionMsg.versionInfo)
}
}
outfile.withWriter{
writer->writer.append(sw.toString())
}
}else{
//已有版本信息
xmlBuild.release{
versionCode(versionMsg.versionCode)
versionName(versionMsg.versionName)
versionInfo(versionMsg.versionInfo)
}
//将生成的数据插入到根节点之前
def lines=outfile.readLines()
def lengths=lines.size()-1
outfile.withWriter{
writer->
lines.eachWithIndex(String line,int index->
if(index!=lengths){
writer.append(line+"\r\n")
}else{
writer.append("\r\n"+sw.toString()+"\r\n")
writer.append(lines.get(lengths))
}
)
}
}
}
}
task readTask{
inputs.file destFile
doLast{
def file=inputs.files.getSingleFile
println file.text
}
}
下面我们可以通过编写一个测试代码测试脚本是否能正确执行
task verTest{
dependsOn writeTask,readTask
doLast{
println '完成输出......'
}
}
注意:dependsOn强依赖方式和通过task指定输入输出方式是等效的
- 通过API指定执行顺序
task taskX{
doLast{
println 'taskX'
}
}
task taskY{
doLast{
println 'taskY'
}
}
task taskZ{
doLast{
println 'taskZ'
}
}
为它们三个指定执行逻辑,X->Y-Z,那么我们可以使用
- mustRunAfter
task taskX{
doLast{
println 'taskX'
}
}
task taskZ{
mustRunAfter taskY
doLast{
println 'taskZ'
}
}
task taskY{
mustRunAfter taskX
doLast{
println 'taskY'
}
}
- shouldRunAfter
task taskX{
doLast{
println 'taskX'
}
}
task taskZ{
shouldRunAfter taskY
doLast{
println 'taskZ'
}
}
task taskY{
shouldRunAfter taskX
doLast{
println 'taskY'
}
}
mustRunAfter和shouldRunAfter作用是一样的,但是must具有强制性
挂接task到构建过程中
首先我们将之前的代码抽离成一个独立gradle文件
versioncotrol.gradle
import groovy.xml.MakeUpBuilder
ext{
versionName='1.0.0'
versionCode='100'
versionInfo='新版本上线'
destFile=file('versioncotrol.xml')
if(destFile!=null&&destFile.exists()){
destFile.createNewFile()
}
}
task wirteTask{
//指定输入
inputs.property('versionCode',this.versionCode)
inputs.property('versionName',this.versionName)
inputs.property('versionInfo',this.versionInfo)
//指定输出
outputs.file destFile
doLast{
//获取数据
def data=inputs.getProperties()
File outfile=outputs.getFiles().getSingleFile()
//将map类型的data数据转换成实体对象
def versionMsg=new VersionMsg(data)
//将实体对象转换成xml格式
def sw=new StringWriter()
def xmlBuild=new MakeUpBuilder(sw)
if(outfile.text!=null && outfile.text.size<=0){
//文件中没有内容
xmlBuild.releases{
release{
versionCode(versionMsg.versionCode)
versionName(versionMsg.versionName)
versionInfo(versionMsg.versionInfo)
}
}
outfile.withWriter{
writer->writer.append(sw.toString())
}
}else{
//已有版本信息
xmlBuild.release{
versionCode(versionMsg.versionCode)
versionName(versionMsg.versionName)
versionInfo(versionMsg.versionInfo)
}
//将生成的数据插入到根节点之前
def lines=outfile.readLines()
def lengths=lines.size()-1
outfile.withWriter{
writer->
lines.eachWithIndex(String line,int index->
if(index!=lengths){
writer.append(line+"\r\n")
}else{
writer.append("\r\n"+sw.toString()+"\r\n")
writer.append(lines.get(lengths))
}
)
}
}
}
}
task readTask{
inputs.file destFile
doLast{
def file=inputs.files.getSingleFile
println file.text
}
}
//开始挂接
this.project.afterEvaluate{project->
def buildTask=project.tasks.getByName('build')
if(buildTask==null) {
throws GradleException('the build task is not found')
}
buildTask.doLast{
//在build任务执行后将执行wirteTask任务
wirteTask.execute()
}
}
然后在app.gradle引入这个被抽离的gradle文件即可
学习Gradle挂载更多内容,请阅读Tinker源码
总结
- task可以在执行阶段执行,但是一定需要添加doFirst或者doLast方法
- task执行顺序
- 通过tinker源码了解task挂接