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執行順序指定的三種方式:
- dependsOn強依賴方式
- 通過Task輸入輸出指定(與第1種等效)
- 通過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
複製程式碼