學點Groovy來理解build.gradle代碼

在寫這篇博客時,搜索參考了不少資料,網上對於 Groovy 介紹的博客已經特別多了,因此也就沒準備再詳細的去介紹 Groovy,原本也就計劃寫一些本身認爲較重要的點。後來發現了 Groovy 的官方文檔後,發現其實官方的介紹特別的全面,詳細。但惋惜的是個人英語很差,看英文文檔有些費時間,但仍是推薦有能力的人去參照官方文檔來學習,後期若是有時間的話,我也計劃試着翻譯一些官方的文檔來學習,記錄一下。html

因此,這篇的側重點不是在介紹 groovy 的基本語法,而是介紹跟 build.gradle 比較相關的一些知識點吧,另外在末尾會附上一些 groovy 學習連接,有興趣的能夠繼續去學習。java


系列索引

build.gradle系列一:看不懂的build.gradle代碼
build.gradle系列二:學點Groovy來理解build.gradle代碼
build.gradle系列三:如何用Adnroid Studio查看build.gradle源碼
...android


開始學習 Groovy 前,引用徐宜生的《Android羣英傳:神兵利器》書中的一句話來介紹 Groovy:數組

Groovy 對於 Gradle,就比如 Java 對於 Android。瞭解一些基本的 Groovy 知識,對於掌握 Gradle 是很是有必要的。閉包

Groovy 是什麼

Groovy 是一種腳本語言,既然是腳本語言,那麼它也就有腳本語言的那些特色:使用動態類型、末尾不用分號等等。另外,它又是基於 Java 上設計的語言,也就是 Groovy 兼容 Java,可使用 JDK 裏的各類方法,你能夠在 Groovy 文件裏寫 Java 代碼裏,照樣能夠正常編譯運行。app

Groovy 語法

關於語法的詳細的介紹在末尾有連接,這裏就只是挑出我認爲比較重要的,並且跟 java 有區別的,在閱讀代碼時可能會看不懂的一些語法進行記錄。學習

1.註釋、標識符方面跟 Java 基本同樣。

2.基本數據類型,運算方面

這方面在 build.gradle 文件裏也不怎麼常見到使用,由於 groovy 是動態類型,定義任何類型均可以只使用 def 來定義,因此若是使用具體的好比 char, int 等類型時須要強制轉換吧。有須要的能夠本身查閱末尾的參考連接。gradle

3.字符串方面

java 只支持用 "..." 雙引號來表示字符串ui

groovy 支持使用 '...', "...", '''...''', """...""", /.../, $/.../$ 即單引號,雙引號等6種方法來表示字符串
至於各類表示方法有什麼區別,具體能夠參考末尾的連接,這裏簡單提提,'...', "..." 只支持單行字符串,不支持多行,剩下的四種都支持多行字符串,以下圖
Groovy字符串代碼示例
控制檯輸出結果url

斜槓我也不多見,常見的是帶有 ${} 的字符串,好比: println "blog's url: ${blogUrl}" 這是 groovy 的 GString 特性,支持字符串插值,有點了相似於變量引用的概念,但注意,在 '...', '''...''' 單引號表示的字符串裏不支持 ${}。固然,若是你要使用 java 的方式,用 + 來拼接也能夠。

4.集合方面(List、Map)

定義和初始化
定義很簡單,List 的話使用 [] 定義,各項用 , 隔開便可。Map 的話使用 [:],各項也是用 , 隔開,如:

def numList = [1, 2, 3]  //List
def map [1:"dasu", dasu:24] //Map, : 前是key,如1, : 後是value, 如dasu

有一點跟 java 不一樣的是, groovy 集合裏不要求每一項都是同類型,好比能夠這樣定義 def list = [1, 'dasu', true],集合裏包含數字,字符串,布爾值三種類型。

使用
經過下標操做符 [] 讀寫元素值,並使用正索引值訪問列表元素或負索引值從列表尾部訪問元素,也可使用範圍,或使用左移 << 追加列表元素,如

//========= List 使用 ================
println numList[1]  //輸出 1
println numList[-1] //輸出 3

numList[2] = 4    // println numList[2]將輸出 4
numList[3] = 5
numList << "dasu" //如今numList = [1, 2, 4, 5, "dasu"]

//========== Map 使用 ================
println map[1]       //輸出 dasu
println map.dasu     //輸出 24, key是字符串的話能夠這樣訪問
map[3] = "I am dasu" // 在map里加入一個[3:"I am dasu"]項

跟 java 不一樣的是, groovy 並不存在下標訪問越界,當下標爲負數時則從右開始算起,當指定的下標沒有存放值時返回 null。

5.數組方面

groovy 其實沒有嚴格區分數組和集合,數組的定義和使用方法跟集合同樣,只是你須要強制聲明爲數組,不然默認爲集合,如

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  
def numArr = [1, 2, 3] as int[] //as 是 groovy 關鍵字

上面的初始化方式是否是跟 java 不同,這一點須要注意下,java 是用 {} 來初始化,但在 groovy 裏面, {} 表示的是閉包,因此這點須要注意一下。


上面的是 groovy 與 java 不一樣的一些基本語法,下面介紹一些我本身認爲是 groovy 比較重要的特性,若是要看懂 build.gradle 裏的代碼,明白下面介紹的會比較有幫助。

6.方法的簡化使用

方法的括號能夠省略

groovy 定義方法時能夠不聲明返回類型和參數類型,也能夠不須要 return 語句,最後一行代碼默認就是返回值。
而在調用方法時能夠將括號省略,不省略的時候以下

def add(a, b) {
    a + b
}
println add(1,2)  //輸出 3

上面的方式不陌生吧,再來看看下面的代碼

println add 1, 2 //輸出 3, add方法同上

上面就是調用方法時省略掉圓括號的寫法,再來看一種狀況

def getValue(Map map) {
    map.each {
        println it.key + ":" + it.value
    }
}
def map = [author:"dasu"]
getValue(map) //輸出 author:dasu

此次定義一個參數爲 map 類型的方法,若是咱們在調用方法的時候纔對參數進行定義和初始化會是什麼樣的呢?以下

getValue(author: "dasu") //輸出 author:dasu

以前說過了,groovy 調用方法時能夠將括號省略掉,這樣一來再看下

getValue author: "dasu" //輸出 author:dasu

這樣子的格式是否是看着以爲很眼熟,沒錯,就是 build.gradle 裏的第一行代碼。
build.gradle
若是有看過個人上一篇 build.gradle 博客的話,如今對疑問1是否是就有些理解了呢。

上圖那代碼若是把省略的括號補上的話,你們應該就會熟悉點了

// apply plugin: 'com.android.application'  等效於
def map = [plugin: 'com.android.application']
apply(map)

調用了 apply() 方法,該方法傳入一個 map 參數,咱們來看看是否是這樣,用as查看下源碼,以下
PluginAware.java
沒錯吧,apply() 實際上是個方法,參數爲 map 類型,並且 key 的取值也給你規定了 from, plugin, to 三種,是否是確實在別人的 build.gradle 代碼裏也有看見過相似 apply from ***,這樣一來就明白多了吧。

好了,而後你再從新去看一下 build.gradle 裏的代碼,是否是對每一行的代碼都有了新的見解了。

其實 build.gradle 裏的每一行代碼都是在調用一個方法,好比下面這些咱們常見的:
build.gradle
每一行都是在調用一個方法,前面是方法名,後面是方法的參數,只是把括號省略掉了而已,感興趣的你能夠再本身用as點進去看看源碼是否是這樣。

方法最後一個參數是閉包能夠提取出來接到後面

閉包是 groovy 的一大特性,我理解也不深,也講不大清楚,感興趣的可自行網上查閱學習,簡單的說就是一個用 {..} 包起來的代碼塊,好比 build.gradle 裏的 defaultConfig{...}, buildTypes{...}, dependencies{...} 等等這些大括號包起來的代碼塊就是閉包,閉包代碼塊最後一句代碼做爲閉包的返回值。

當閉包做爲方法的最後一個參數,能夠將閉包從參數圓括號中提取出來接在最後,若是閉包是惟一的一個參數,則方法參數所在的圓括號也能夠省略。對於有多個閉包參數的,只要是在參數聲明最後的,都可以按上述方式省略,舉個例子。

//定義 add 方法
def add(a, Closure c) {
println a + c.call()
}
//調用方法
add(1, {1+1}) //輸出 3

上面定義一個 add 方法,最後一個參數爲閉包,調用的時候傳入一個閉包,閉包的最後一行代碼 1+1 做爲閉包返回值返回,閉包返回值做爲方法的第二個參數傳入方法中計算加法,因此最終輸出3。上面的調用也能夠寫成下面的方式:

add(1){
    1+2
} //輸出 4

注意,這是調用 add() 方法,而不是在定義,1 是第一個參數,括號後的閉包 { 1+2 } 是方法的第二個參數,這就是 groovy 的特性,閉包能夠提取出來。那麼再想一想,若是方法只有一個閉包參數,再結合 groovy 能夠省略掉括號的特性,這樣子調用一個方法將會是什麼樣子呢?

//定義 method 方法
def method(Closure c) {
    println c.call()
}
//調用方法
method {
    I'm dasu
} //輸出 I'm dasu

是否是又感受很熟悉,對吧,就是 build.gradle 裏的 defaultConfig{...}, buildTypes{...}, dependencies{...} 等等這些。

因此,結合上面講的兩點:能夠省略方法括號和閉包能夠提取接到括號後面,這樣一來, build.gradle 裏的代碼其實就是在調用各類方法,defaultConfig 是一個方法,compileSdkVersion 也是一個方法。 build.gradle 裏的每一行代碼前面是方法名,後面則是方法須要的參數,參數有的是基本類型,有的則是閉包類型。

集合遍歷 each/all
就先把上一篇博客裏的在一段在 build.gradle 裏很常見的代碼貼出來

重名名apk代碼

這段代碼做用就是對打包生成的 apk 按照規定的格式進行重命名,在不少大神的 build.gradle 裏都會碰見過,其實這一段代碼就是 groovy 代碼,alleach 是集合的一種操做,all 後面跟着的是一個參數爲 variant 的閉包,表示對 applicationVariants 集合裏全部的對象都運行後面的閉包,同理 each 後面也是跟着一個參數爲 output 的閉包,相似於 java 裏的 for 循環操做。因此這裏要理解的應該是 applicationVariants 表明的是什麼,這點我也還不是很懂,後面若是搞懂了的話會在以後的博客裏介紹出來。

另外,我還有個疑問來着, all 操做和 each 操做有什麼區別麼,感受都是對集合裏全部的元素進行操做,若是有懂的可以告知就太感謝了,查了挺多資料貌似還不是很明白。

參考資料

官方文檔
Groovy語言規範-語法(官方文檔翻譯)
Groovy操縱集合祕籍


QQ圖片20180316094923.jpg 最近剛開通了公衆號,想激勵本身堅持寫做下去,初期主要分享原創的Android或Android-Tv方面的小知識,感興趣的能夠點一波關注,謝謝支持~~

相關文章
相關標籤/搜索