原文發於微信公衆號 jzman-blog,歡迎關注交流。
前面幾篇學習了 Gradle 構建任務的基礎知識,瞭解了 Project 和 Task 這兩個概念,建議先閱讀前面幾篇文章:java
Gradle 的構建工做是有一系列的 Task 來完成的,本文將針對 Task 進行詳細介紹,本文主要內容以下:微信
Gradle 中能夠使用多種方式來建立任務,多種建立任務的方式最終反映在 Project 提供的快捷方法以及內置的 TaskContainer 提供的 create 方法,下面是幾種常見的建立任務的方式:閉包
/** * 第一種建立任務方式: * 方法原型:Task task(String name) throws InvalidUserDataException; */ //定義Task變量接收task()方法建立的Task,方法配置建立的Task def Task taskA = task(taskA) //配置建立的Task taskA.doFirst { println "第一種建立任務的方式" } /**task * 第二種建立任務方式:可在Map參數中進行相關配置,如依賴、任務描述、組別等 * 方法原型:Task task(Map<String, ?> args, String name) throws InvalidUserDataException; */ def Task taskB = task(group: BasePlugin.BUILD_GROUP,taskB,description: "描述") //配置建立的Task taskB.doLast { println "第二種建立任務的方式" println "任務taskB分組:${taskB.group}" println "任務taskB描述:${taskB.description}" } /** * 第三種建立任務方式:經過閉包的方式建立Task,閉包裏的委託對象就是Task,便可在閉包內調用Task * 的一切屬性和方法來進行Task的配置 * 方法原型:Task task(String name, Closure configureClosure); */ task taskC{ description 'taskC的描述' group BasePlugin.BUILD_GROUP doFirst{ println "第三種建立任務的方式" println "任務taskC分組:${group}" println "任務taskC描述:${description}" } } /** * 第四種建立任務的方式:可在閉包中靈活配置,也可在Map參數中配置,閉包中中的配置父覆蓋Map中相同的配置 * 方法原型:Task task(Map<String, ?> args, String name, Closure configureClosure); */ def Task taskD = task(group: BasePlugin.BUILD_GROUP,taskD,description: "描述"){ description 'taskD的描述' group BasePlugin.UPLOAD_GROUP doFirst{ println "第四種建立任務的方式" println "任務taskD分組:${group}" println "任務taskD描述:${description}" } }
上面是建立任務的四種方式,使用時選擇合適的建立方式便可,上面提到 Map 中能夠配置 Task 的相關參數,下面是是 Map 中可以使用的配置:post
type:基於一個已存在的Task來建立,相似於類的繼承,默認值DefaultTask overwrite:是否替換存在的Task,通常和type配合使用,默認值false dependsOn:配置當前任務的依賴,默認值[] action:添加到任務中的一個Action或者是一個閉包,默認值爲null description:任務描述,默認值null group:任務分組,默認值null
經過閉包的方式建立 Task,閉包裏的委託對象就是 Task,便可在閉包內調用 Task
的一切屬性和方法來進行 Task 的配置,能夠說使用閉包的這種任務建立方式更靈活,此外還能夠使用 TaskContainer 建立任務,參考以下:學習
//使用TaskContainer建立任務的方式 tasks.create("taskE"){ description 'taskE的描述' group BasePlugin.BUILD_GROUP doFirst{ println "第三種建立任務的方式" println "任務taskE分組:${group}" println "任務taskE描述:${description}" } }
tasks 是 Project 的屬性,其類型是 TaskContainer,因此能夠經過 tasks 來建立任務,固然 TaskContainer 建立任務也有建立任務的其餘構造方法,到此關於任務的建立就基本介紹完了。測試
建立的任務(Task)屬於項目(Project)的一個屬性,其屬性名就是任務名,該屬性的類型是 Task,若是已知任務名稱,那麼能夠經過任務名直接訪問和操縱該任務了,也能夠理解訪問和操縱該任務所對應的 Task 對象,參考
以下:gradle
/** * 訪問任務的第一種方式:Task名稱.doLast{} */ task taskF{ } taskF.doLast{ println "第一種訪問任務的方式" }
任務都是經過 TaskContainer 的 create 方法建立的,而 TaskContainer 是建立任務的集合,在 Project 中可經過 tasks 屬性訪問 TaskContainer ,tasks 的類型就是 TaskContainer,因此對於已經建立的任務可經過訪問幾何元素的方式訪問已建立任務的屬性和方法,參考代碼以下:ui
/** * 訪問任務的第二種方式:使用TaskContainer訪問任務 */ task taskG{ } tasks['taskG'].doLast { println "第二種訪問任務的方式" }
在 Groovy 中 [] 也是一個操做符,上面 tasks['taskG'] 的真正含義是 tasks.getAt('taskG') , getAt() 方法在 TaskCollection 中的方法,這樣能夠經過任務名稱對相關任務進行訪問和操做。lua
還能夠經過路徑訪問的方式訪問任務,經過路徑訪問任務有兩個關鍵方法:findByPath 和 getByPath,區別在於前者找不到指定任務的時候會返回 null,後者找不到任務的時候會拋出 UnknowTaskException 異常,代碼參考以下:spa
/** * 訪問任務的第三種方式:使用路徑訪問任務 */ task taskH{ println 'taskH' //經過路徑訪問任務,參數能夠是路徑(沒有訪問成功,寫法以下) println tasks.findByPath(':GradleTask:taskG') //經過路徑訪問任務,參數能夠是任務名稱 println tasks.findByPath('taskG') println tasks.getByPath('taskG') }
上述代碼執行結果參考以下:
PS E:\Gradle\study\GradleTask> gradle taskH > Configure project : taskH null task ':taskG' task ':taskG' FAILURE: Build failed with an exception. * Where: Build file 'E:\Gradle\study\GradleTask\build.gradle' line: 98 * What went wrong: A problem occurred evaluating root project 'GradleTask'. > Task with path 'test' not found in root project 'GradleTask'. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. * Get more help at https://help.gradle.org BUILD FAILED in 2s
使用路徑訪問任務的過程,參數寫成路徑訪問不到具體的任務,多是寫法問題,但願在後面的學習中可以解決。
此外,還能夠經過任務名稱訪問,方法主要是 findByName 和 getByName,區別和第三種訪問方式同樣, 代碼參考以下:
/** * 訪問任務的第四種方式:使用任務名稱訪問 */ task taskJ tasks['taskJ'].doLast{ println 'taskJ' println tasks.findByName('taskJ') println tasks.getByName('taskJ') }
上面學習了訪問任務的四種方式,經過對 Gradle 訪問任務的瞭解,在具體的項目構建上在結合上面訪問任務的方式靈活使用。
對於任務分組及描述實際上在以前的文章已經提到過且配置過,這裏再簡單說明一下,任務分組和描述實際上就是對已經建立的任務配置分組和任務描述,以下面這樣配置:
//任務分組與描述 def Task task1 = task taskK task1.group = BasePlugin.BUILD_GROUP task1.description = '測試任務分組與描述' task1.doLast { println "taskK is group = ${group}, description = ${description}" }
下面是上述代碼執行結果,參考以下:
PS E:\Gradle\study\GradleTask> gradle taskK > Task :taskK taskK is group = build, description = 測試任務分組與描述 BUILD SUCCESSFUL in 2s 1 actionable task: 1 executed
從執行結果能夠看出,若是配置了任務的相關屬性,那麼就能夠訪問到任務的全部信息了。
學習一個操做符 << ,以前的測試代碼中爲了測試都會調用 task.doLast() 方法,咱們能夠使用 << 操做符來代替 doLast 方法,也就是說 daLast() 方法能夠這樣寫:
//<< 任務操做符 //簡寫方式,Gradle 5.0 開始以不推薦這種寫法 task taskL <<{ println "doLast" } //推薦寫法 task taskL{ doLast{ println "doLast" } }
上述兩種寫法的執行結果參考以下:
PS E:\Gradle\study\GradleTask> gradle taskL > Configure project : The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead. at build_6syzx8ks0l09hby4j6yn247u9.run(E:\Gradle\study\GradleTask\build.gradle:123) > Task :taskL doLast BUILD SUCCESSFUL in 2s 1 actionable task: 1 executed PS E:\Gradle\study\GradleTask> gradle taskL > Task :taskL doLast BUILD SUCCESSFUL in 2s 1 actionable task: 1 executed PS E:\Gradle\study\GradleTask>
從輸出結果能夠看到兩種寫法都輸出了想要的結果,同時觀察日誌發現這種簡寫方式已經在 Gradle 5.0 開始已經被放棄,因此推薦搭建使用 doLast 的方式配置任務。
在 Gradle 任務執行過程當中,咱們能夠經過 doFirst 和 doLast 在任務執行以前或執行以後進行任務相關配置,當咱們執行一個任務的時候,其實是在執行該 Task 所擁有的 action,能夠經過 getActions() 方法獲取全部能夠執行的 action,下面自定義一個 Task 類型 CustomTask ,並使用註解 @TaskAction 標註方法 doSelf 表示 Task 自己要執行的方法,代碼以下:
//任務執行流程分析 def Task taskA = task taskB(type: CustomTask) taskA.doFirst { println "Task執行以前調用:doFirst" } taskA.doLast { println "Task執行以後調用:doLast" } class CustomTask extends DefaultTask{ @TaskAction def doSelf(){ println "Task執行自己調用:doSelf" } }
上述代碼的執行結果以下:
PS E:\Gradle\study\GradleTask2> gradle taskB > Task :taskB Task執行以前調用:doFirst Task執行自己調用:doSelf Task執行以後調用:doLast BUILD SUCCESSFUL in 2s 1 actionable task: 1 executed
因爲 Task 的執行是在遍歷須要執行的 action 列表,爲了保證執行的順序,則必須將 doFirst 對應的 action 放在 action 列表的最前面,doLast 對應的 action 放在 action 列表的最後面,doSelf 對應的 action 放置在列表的中間位置,這樣就能保證對應的執行順序了,下面是僞代碼:
//建立任務的時候將使用@TaskAction標註的方法做爲Task自己執行的Task //此時,任務正在建立,actionList裏面只有Task自己執行的Action actionList.add(0,doSelfAction) //任務建立完成以後,若是設置了doFirst則會在actionList最前面添加doFist對應的action //此時,doFirst對應的action添加actionList的最前面,保證了doFirst方法在任務開始執行以前執行 actionList.add(0,doFirstAction) //任務建立完成以後,若是設置了doLast則會在actionList最後面添加doLast對應的action,保證了doLast方法在任務開始執行以後執行 actionList.add(doLastAction)
任務執行的流程基本如上,儘可能在具體實踐中多體會。
Gradle 中任務排序使用到的是 Task 的兩個方法 shoundRunAfter 和 mustRunAfter,能夠方便的控制兩個任務誰先執行:
/** * 任務順序 * taskC.shouldRunAfter(taskD):表示taskC要在taskD的後面執行 * taskC.mustRunAfter(taskD):表示taskC必需要在taskD的後面執行 */ task taskC { doFirst{ println "taskC" } } task taskD { doFirst{ println "taskD" } } taskC.shouldRunAfter(taskD)
上述代碼的執行結果,參考以下:
PS E:\Gradle\study\GradleTask2> gradle taskC taskD > Task :taskD taskD > Task :taskC taskC BUILD SUCCESSFUL in 2s 2 actionable tasks: 2 executed
Task 中有個 enabled 屬性,能夠使用該屬性啓用和禁用某個任務,設置爲 true 則啓用該任務,反之則禁用該任務,該屬性默認爲 true,使用以下所示:
taskA.enabled = true
斷言是一個條件表達式, Task 對象有一個 onlyIf 方法,該方法能夠接收一個閉包做爲參數,若是該閉包內參數返回 true,則該任務執行,反之則不執行該任務,這樣能夠經過任務的斷言來控制那些任務須要執行,下面經過一個打包的案列來學習任務的斷言,代碼參考以下:
//任務的onlyIf斷言 final String BUILD_ALL = 'all' final String BUILD_FIRST = 'first' final String BUILD_OTHERS = 'others' task taskTencentRelease{ doLast{ println "打應用寶渠道包" } } task taskBaiduRelease{ doLast{ println "打百度手機助手渠道包" } } task taskMiuiRelease{ doLast{ println "打小米應用商店渠道包" } } task buildTask{ group BasePlugin.BUILD_GROUP description "打渠道包" } //爲buildTask添加依賴的具體任務 buildTask.dependsOn taskTencentRelease, taskBaiduRelease, taskMiuiRelease taskTencentRelease.onlyIf{ if (project.hasProperty("buildApp")){ Object buildApp = project.property("buildApp") return BUILD_ALL == buildApp || BUILD_FIRST == buildApp }else{ return true } } taskBaiduRelease.onlyIf{ if (project.hasProperty("buildApp")){ Object buildApp = project.property("buildApp") return BUILD_ALL == buildApp || BUILD_FIRST == buildApp }else{ return true } } taskMiuiRelease.onlyIf{ if (project.hasProperty("buildApp")){ Object buildApp = project.property("buildApp") return BUILD_OTHERS == buildApp || BUILD_ALL == buildApp }else{ return true } }
下面是上述代碼的執行結果:
PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=first buildTask > Task :taskBaiduRelease 打百度手機助手渠道包 > Task :taskTencentRelease 打應用寶渠道包 BUILD SUCCESSFUL in 1s 2 actionable tasks: 2 executed PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=others buildTask > Task :taskMiuiRelease 打小米應用商店渠道包 BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed PS E:\Gradle\study\GradleTask2> gradle -PbuildApp=all buildTask > Task :taskBaiduRelease 打百度手機助手渠道包 > Task :taskMiuiRelease 打小米應用商店渠道包 > Task :taskTencentRelease 打應用寶渠道包 BUILD SUCCESSFUL in 1s 3 actionable tasks: 3 executed
能夠看出,當咱們執行 buildTask 時爲 Project 配置了屬性 buildApp,經過 buildApp 不一樣的值,藉助 onlyIf 實現了不一樣渠道包的定製打包策略,可在實際開發中借鑑加以使用。
此外,注意上述代碼執行命令的寫法,具體以下:
//其中buildApp和=後面的值others是鍵值對的關係,使用命令執行任務時可以使用-P命令簡寫 //-P要爲當前Project指定K-V的屬性鍵值對,即-PK=V gradle -PbuildApp=others buildTask
建立的任務都是在 TaskContain 裏面,我麼能夠經過從 Project 的屬性 tasks 中根據任務的名稱來獲取想要獲取的任務,能夠經過 TaskContain 的 addRule 方法添加相應的任務規則,參考代碼以下:
//任務規則 tasks.addRule("對該規則的一個描述"){ //在閉包中經常將->做爲參數與代碼塊之間的分隔符 String taskName -> task(taskName) { doLast{ println "${taskName} 不存在" } } } task taskTest{ dependsOn taskX }
上述代碼的執行結果:
PS E:\Gradle\study\GradleTask2> gradle taskTest > Task :taskX taskX 不存在 BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed
若是不指定對某些特殊狀況的任務處理,則會報錯,若是處理了則會輸出相關的提示信息,Gradle 任務的瞭解和學習就到此爲止。能夠關注公衆號:躬行之(jzman-blog),一塊兒交流學習。