Gradle 理解 (三):Task
task在gradle佔有很重要的地位,因為在gradle中任何執行操作都是通過task來執行。task可以理解成任務,作用就是執行某些指定的操作。
以Android為例,Gradle構建編譯一個Android專案的時候,需要執行很多操作流程。整個過程可以通過命令列gradle assembleRelease --info
來看一下:
我們會看到很多task,這些task就是一個一個任務,裡面執行特定的操作流程,比如preBuild負責準備配置檔案,preReleaseUnitTestBuild負責準備release下的單元測試構建,generateReleaseResources準備資原始檔等等,一系列的task執行起來,構建一個完整的apk檔案。
一. project
在深入理解task之前,還需要理解另外一個重要而又跟task很大關聯的東西:project。
每個可以構建編譯的模組就是一個project。還是以Android為例,無論是library,application都是一個project,更簡單的理解就是看這個模組是否有build.gradle檔案,如果有那麼就是一個project。因為project有構建編譯等其他的需求,那麼自然就需要task,所以project裡面包含至少一個task。
為什麼說有的build.gradle的模組就是一個project?因為Gradle會預設為每個build.gradle根據配置分配一個project物件,所有我們也能在build.gradle裡面預設使用project物件及相對應的API。在build.gradle中,你可以通過API來訪問gradle的所有特性,比如task的建立以及依賴管理,在實際開發中,你也會接觸到很多API。
下面列出一個project的常用的API圖:
這裡面的API我們都可以直接在build.gradle裡面直接使用,而且不需要例項化project物件,因為每個build.gradle都是分配一個project物件。
通過以上,大概也知道project以及project和task之間的關係了。如果還是不明白,打個比喻:project就是一個公司部門,task就是部分的員工,project的運作只有靠一個個task各司其職才能運作起來。
二. task
2.1 action
上面我們提到task的作用就是執行操作,那麼執行操作的動作也就是放置構建邏輯的地方應該如何新增呢?task有個概念-action,就是要執行的動作,內部通過一個集合管理action,task執行的時候像訊息佇列一樣,一個一個action消費執行掉。task提供了doFirst和doLast兩個函式來新增action:doFirst ,doLast ,用於新增需要最先執行的Action和需要和需要最後執行的Action。 看個例子:
task helllTask {
doFirst {
println "Task doFirst"
}
doLast {
println "Task doLast"
}
}
我們給helllTask上新增兩個action,分別是
{
println "Task doFirst"
}
{
println "Task doLast"
}
還是套用之前的部分跟員工的例子,task是一個一個員工的話,那麼action就是分配的任務,分別可以選擇上班的時候(doFirst)做跟下班的時候做(doLast)。
2.2 依賴
task 有時候需要執行很多很多操作,有些操作跟其他的task是一樣的,這時候就可以將這些相同操作的task抽取出來一個基礎的task,然後其他的task分別依賴這個基礎的task。
task One {
doFirst {
println "一言不合先來個1"
}
}
task Two {
doFirst {
println "一言不合先來個2"
}
}
task Three(dependsOn: [One, Two]) {
doFirst {
println "我需要1跟2"
}
}
終端執行:gradle Three
得到:
> Task :One
一言不合先來個1
> Task :Two
一言不合先來個2
> Task :Three
我需要1跟2
Three依賴了One, Two,在執行的時候先執行One,Two再執行Three。但是注意一個點,不要被dependsOn: [One, Two] 這樣的寫法迷惑了,不要以為依賴的執行順序就是從左到右執行,正確的是依賴的執行順序是不確定。
task One {
doFirst {
println "一言不合先來個1"
}
}
task Two {
doFirst {
println "一言不合先來個2"
}
}
task Four {
doFirst {
println "一言不合先來個4"
}
}
task Three(dependsOn: [One, Two,Four]) {
doFirst {
println "我需要1跟2跟4"
}
}
終端執行:gradle Three
得到:
> Task :Four
一言不合先來個4
> Task :One
一言不合先來個1
> Task :Two
一言不合先來個2
> Task :Three
我需要1跟2跟4
雖然Four是放在最右邊的,但是執行卻是第一個執行的。為什麼task依賴執行是無序呢?因為這些依賴task執行的時候是並行的,這樣在編譯構建的時候,並行執行能夠極大的節約編譯執行時間。
這時候你可能會想,如果需要指定一個task一定要執行在另外一個task前面呢,比如先執行單元測試,再執行打包。
這種需求是正常的,gradle肯定也是考慮到的。要實現task順序,有兩種方式:
- shouldRunAfter/mustRunAfter
taskA.shouldRunAfter(taskB):表示taskB應該在taskA執行後執行。主要這裡是should,不是必須要的,也就是還是可能會出現不會按照預設那樣執行。
taskA.mustRunAfter(taskB):表示taskB必須在taskA執行後執行。這裡是must,一定會按照預設那樣執行。
-
finalizedBy
finalizedBy 也是task的終結器。就是指定一個task在另外一個task結束後必須執行。
task A { doLast { print "AAAAAA" } } task B { doLast { print "BBBBB" } } A.finalizedBy B
終端執行:gradle A
Task :A AAAAAA Task :B BBBBB
2.3 生命週期
很多框架跟元件都有自己的生命週期,Gradle也有生命週期。Gradle生命週期分為三個部分:初始化階段,配置階段,執行階段。
-
初始化極端
在初始化階段,Gradle為每個build.gradle分配一個project例項物件,初始化階段相關類似setting.gradle
的指令碼檔案 ,然後根據正在執行的專案,找出哪些專案依賴需要參與到構建中來。
-
配置階段
初始化階段結束後緊跟就是配置階段。配置階段的任務是執行各專案下的build.gradle指令碼,完成Project的配置,並且構造Task任務依賴關係圖以便在執行階段按照依賴關係執行Task。 Gradle構造了一個模型來表示任務,並且參與到任務構建中來。增量式構建特性決定了模型中的task是否需要被執行。注意,專案中每一次構建的任何配置程式碼都可以被執行。
-
執行階段
在執行階段,就是將配置階段task關係依賴順序進行執行。因為是增量構建,如果一個task如果被認為沒有修改過則不會執行。