Gradle對於咱們大多數Android開發者能夠說是熟悉又陌生。但這個理由並不足以讓我有動力寫下這篇文章,其主要由於是這樣的:最近參與項目開發,發現項目工程裏的的build.gradle比以前接觸的的複雜了許多,雖然大部份內容經過表面英文也勉強能看懂,但仍是有些內容會讓人懵逼。尤爲是gradle出錯的時候,我第一時間會運用一個很是熟練的技能,ctrl+c問題,ctrl+v到google,而後經過google出來的結果跟着改,運氣不錯喲,這麼簡單的改動就gradle經過了,這時喜悅以前噴涌而出,嘴角開始瘋狂的上揚。但其實心裏是很虛的,由於你根本不知道是基於什麼原理去解決的。因此總的來講本篇博客的目的就是兩個:數據結構
減小無效的ctrl+c/v時間
弄懂gradle的使命,遇到gradle問題再也不那麼虛
本篇博客的核心內容分三點:閉包
Groovy和Java
Gradle的生命週期
task的定義
Groovy和Java
在學習gradle的過程當中最好記住下面一句話,避免在gradle的摩天大樓中迷失了方向jvm
Gradle腳本是基於Groovy語音的,於是它必定要遵循Groovy的語法ide
Groovy是一門腳本語言,也是一門JVM語言,它最終是要編譯成class文件而後在jvm上執行,因此Java語言的特性Groovy都支持,咱們徹底能夠混寫Java和Groovy。咦,怎麼這段話好像在介紹kotlin見過呀!是的,若是你學過kotlin你會發現,kotlin和groovy的不少語法規則都同樣。函數
既然如此,那Groovy的優點是什麼呢?簡單來講:Groovy提供了更加靈活簡單的語法,大量的語法糖以及閉包特性可讓你用更少的代碼來實現和Java一樣的功能。好比解析xml文件,Groovy就很是方便,只須要幾行代碼就能搞定,用Java則須要幾十行代碼。學習
下面只簡單的介紹Groovy的幾個語法,更多的語法能夠參考官方文檔,本文的重點再也不這裏。gradle
def 關鍵字來聲明變量和函數
def a = 1 //省略了分號
def b = "hello world"
def int c = 1 ui
def hello(msg) { //參數類型可省略 println (msg) 1 //方法返回值可省略return }
複製代碼
在Groovy中,類型是弱化的,全部的類型均可以動態推斷
在Groovy中不少東西均可以省略,好比語句後的分號,返回值最後的renturn,函數參數的類型google
閉包(Closure)
簡單的理解,經過一對{}把代碼塊框起來,做爲一個總體對象來處理code
{
[closureParameters ->]
sataments
}
複製代碼
[]內容是能夠省略的,但不表明沒有,Groovy自動幫我嗎分析推斷,從而省去人工書寫的繁瑣過程
舉幾個栗子
{ -> item++ } //引用一個變量item { it -> println it } //使用隱含閉包參數it { println it } //省略隱含閉包參數it { name -> println name } //顯示閉包參數 { string x, int y -> //多個閉包參數 printIn "hey ${x} the value is $(y}" }
複製代碼
運算符重載
Groovy中的很多運算符會被映射爲針對對象的常規函數調用。譬如:
a + b 會被Groovy 解釋爲 a.plus (b)
a - b 會被Groovy解釋爲 a.minus(b)
a[b] = c 會被Groovy解釋爲 a.putAt(b, c)
複製代碼
正由於有了這樣的特性,纔有了咱們常見的task語句:
task helloworId <<
{
println 'hello, world
}
複製代碼
Gradle的生命週期
gradle完成它的自動化編譯使命,須要通過三個過程,也就是它的生命週期
初始化
初始化的階段主要職責是定位有哪些須要處理的build文件。若是是單個工程,就會識別出單獨的build文件做爲下一個階段的輸入。若是是多工程,就會找到多個build文件,把它們做爲下一階段的輸入。這個階段settings.gradle文件會被解析。
配置
處理全部的模塊的build腳本,處理依賴,屬性等。對於Android來講每一個模塊的build.gradle文件會被解析並配置,這個時候會構建整個task的鏈表(僅僅指存在依賴關係的task的集合,不是數據結構的鏈表)。這一階段的目標產物其實是由一個個task組成的 DAG圖(Directed Acyclic Graph)
下面有一個栗子能夠直觀的感覺到這個週期過程,傳送門:
全面理解Gradle - 執行時序
Task定義
Task能夠理解爲Gradle的執行單元。Gradle就是經過一個個task來完成具體的構建任務,下面咱們就來看看Task的定義。
task myTask { println "config myTask" }
複製代碼
執行task: gradle myTask 或者 gradle clean
config myTask
能夠看出,只要我執行任何一個task,都會輸出config myTask。這是爲何呢?
由於每一個task執行以前都須要進行一遍完整的配置。
但不少時候咱們都不須要寫配置代碼,那還這麼作不是很影響執行速度?
這個時候能夠經過gradle的提供的一些API方法:
doFirst:task執行最開始的操做
doLast:task執行最後的操做,等價於leftShift操做,因此也能夠寫成這個操做符 <<
那麼把配置代碼和執行代碼分開來能夠寫成這樣:
task myTask { println "config myTask" } myTask << { println "after execute myTask" } myTask.doFirst { println "before execute myTask" }
複製代碼
固然。不少時候在定義task的時候能夠繼承Gradle自己提供的Task,好比Copy、Delete、Sync等。
舉個栗子,繼承Delete的task
task clean(type: Delete) { delete rootProject.buildDir }
複製代碼用於刪除整個project根目錄下的build文件夾