1. 程式人生 > >學點Groovy來理解build.gradle代碼

學點Groovy來理解build.gradle代碼

var 進行 sch ima 再看 基本數據 output 就是 介紹

在寫這篇博客時,搜索參考了很多資料,網上對於 Groovy 介紹的博客已經特別多了,所以也就沒準備再詳細的去介紹 Groovy,本來也就計劃寫一些自己認為較重要的點。後來發現了 Groovy 的官方文檔後,發現其實官方的介紹特別的全面,詳細。但可惜的是我的英語不好,看英文文檔有些費時間,但還是推薦有能力的人去參照官方文檔來學習,後期如果有時間的話,我也計劃試著翻譯一些官方的文檔來學習,記錄一下。

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


系列索引

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


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

Groovy 對於 Gradle,就好比 Java 對於 Android。了解一些基本的 Groovy 知識,對於掌握 Gradle 是非常有必要的。

Groovy 是什麽

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

Groovy 語法

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

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

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

這方面在 build.gradle 文件裏也不怎麽常見到使用,因為 groovy 是動態類型,定義任何類型都可以只使用 def 來定義,所以如果使用具體的比如 char, int 等類型時需要強制轉換吧。有需要的可以自己查閱末尾的參考鏈接。

3.字符串方面

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

groovy 支持使用 ‘...‘, "...", ‘‘‘...‘‘‘

, """...""", /.../, $/.../$ 即單引號,雙引號等6種方法來表示字符串
至於各種表示方法有什麽區別,具體可以參考末尾的鏈接,這裏簡單提提,‘...‘, "..." 只支持單行字符串,不支持多行,剩下的四種都支持多行字符串,如下圖
技術分享圖片
技術分享圖片

斜杠我也很少見,常見的是帶有 ${} 的字符串,比如: 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 博客的話,現在對疑問1是不是就有些理解了呢。

上圖那代碼如果把省略的括號補上的話,大家應該就會熟悉點了

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

調用了 apply() 方法,該方法傳入一個 map 參數,我們來看看是不是這樣,用as查看下源碼,如下
技術分享圖片
沒錯吧,apply() 其實是個方法,參數為 map 類型,而且 key 的取值也給你規定了 from, plugin, to 三種,是不是確實在別人的 build.gradle 代碼裏也有看見過類似 apply from ***,這樣一來就明白多了吧。

好了,然後你再重新去看一下 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 按照規定的格式進行重命名,在很多大神的 build.gradle 裏都會遇見過,其實這一段代碼就是 groovy 代碼,alleach 是集合的一種操作,all 後面跟著的是一個參數為 variant 的閉包,表示對 applicationVariants 集合裏所有的對象都運行後面的閉包,同理 each 後面也是跟著一個參數為 output 的閉包,類似於 java 裏的 for 循環操作。所以這裏要理解的應該是 applicationVariants 代表的是什麽,這點我也還不是很懂,後面如果搞懂了的話會在之後的博客裏介紹出來。

另外,我還有個疑問來著, all 操作和 each 操作有什麽區別麽,感覺都是對集合裏所有的元素進行操作,如果有懂的能夠告知就太感謝了,查了挺多資料貌似還不是很明白。

參考資料

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

學點Groovy來理解build.gradle代碼