Gradle是目前Android主流的構建工具,不管你是通過命令行還是通過Android Studio來build,最終都是通過Gradle來實現的,所以學習Gradle非常重要。
Groovy最終被編譯成class字節碼文件運行在jvm上,所以Java的特性Groovy都支持,但是Groovy提供了一些更加簡潔的語法,比如利用閉包特性來進行文件IO,解析XML,簡直讓你難以置信。
一.開發環境準備
- 方式一:安裝Groovy SDK,安裝方法參考 http://www.groovy-lang.org/download.html,下載後解壓即可,最好是設置一下環境變量。
- 方式二:直接在Android Studio中開發,創建一個普通的Android Project,在build.gradle文件中編寫一個task來執行,示例:
task(jdqm).doLast {
println "Start execute hello."
hello()
}
def hello() {
println "Hello jdqm."
//todo add your code here
}
在hello方法中編寫你的Groovy代碼,然後通過如下命令來執行
gradlew jdqm
作爲Android開發者,個人比較推薦是用第二種方式,畢竟我們的目標是Gradle。
二. 開發實戰
Groovy中的註釋與Java中的一樣,單行註釋//,多行註釋/**/
1.def關鍵字定義變量
def int aInt = 1;
def String aString = "I am a String.";
- 分號不是必須的
- def關鍵字可省略
- 數據類型自動推斷
上面的代碼可以寫成這樣
aInt = 1
aString = "I am a String."
bInt = 2
2.方法的參數類型和返回值類型可省略
def getSomeThing(param1) {
return param1
}
println getSomeThing("Hello Jdqm.")
請注意 println getSomeThing("Hello Jdqm.") 這一行,實際是調用了 println(String)這個方法,但你會發現沒有圓括號,所以方法調用圓括號也不是是必須的,前提是不引起混淆。下面這兩種寫法效果是一樣的:
println getSomeThing("Hello Jdqm.")
println(getSomeThing("Hello Jdqm."))
那什麼時候會混淆,比如
//方法調用省略圓括號,容易引起混淆,getSomeThing 會被當成屬性
println getSomeThing "Hello Jdqm."
3.return關鍵字可省略
//return關鍵字可省略,返回值爲最後一條語句的執行結果
def getSomeThings(param1, param2) {
param1 + param2
}
result = getSomeThings 3, 4
println result // 7
注意:方法的返回值類型和def關鍵字不能同時省略,否則會報找不到方法。
4.字符串
//單引號爲嚴格意義的字符串,輸出 I am $ dollar
String singleQuote(){
return 'I am $ dollar'
}
println singleQuote() // I am $ dollar
//雙引號不是嚴格的字符串,如果其中有$表達式,會先對錶達式求值
String doubleQuote(){
def x = 1
"I am $x dollar"
}
println doubleQuote() // I am 1 dollar
//三引號可以隨意換行
String threeQuote(){
'''line1
line2
line3'''
}
5.自動類型推斷
//自動推斷類型
def x = 1
println x.getClass().getCanonicalName() //java.lang.Integer
6.加強版 List
List可以存放任何類型的數據類型
List emptyList = [] // 通過[]來創建List
List testList=[100, 'hello', true]
println(testList[0]) //100
println(testList[1]) //hello
println(testList[2]) // true
assert testList[0]==100 //true
assert testList[100]==null / /true
//不會有下標越界異常,自動擴展List的大小
testList[100]=10
println testList[100] //10
println testList.size() //101,注意這個List的大小已經變爲101,那麼中間的元素默認值是什麼是什麼?是null
是不是特別像Java中的List<Object>
7.Map
通過[:]來創建,key默認是String,可以不加引號,但是容易引起混淆,單引號雙引號都可以。
Map emptyMap = [:]
Map testMap = ['key1':'value1','key2':'value2']
println(testMap.key1) //value1
println(testMap['key2']) //傳統的取值方式
testMap.anotherkey='anothervalue' //添加一個元素
println testMap.anotherkey //anothervalue
8.Range
def aRange=1..5 //1 2 3 4 5
def bRange=1..<5 //1 2 3 4
println aRange.from //1
println aRange.to //5
println bRange.from //1
println bRange.to //4
其中 aRange.from這種調用方式,實際上是調用了aRange.getFrom()方法。
9.閉包
閉包是一個比較重要的內容。
def aClosure = {String param1, String param2->
println param1
println param2
}
//有兩種調用方式
aClosure.call('hello', 'groovy')
aClosure('hello','groovy')
定義無參數closure,不代表不能傳參數
def bClosure={
println 'hello world of groovy.'
println it // 沒有定義參數的時候,有一個隱含參數it
}
bClosure.call() //不傳參數調用,it爲null
bClosure(100) //it爲100
下面這種寫法表示閉包沒有參數,調用時不能傳入參數,否則會報錯
def cClosure={->
println 'hello world.'
}
//不傳入參數調用,正確
cClosure.call()
//這樣調用會報錯
//cClosure.call('test')
方法的最後一個參數是閉包,可以將這個閉包參數寫到圓括號外
def testClosure(int a, String param, Closure closure){
println ("$a,$param")
closure.call()
}
//傳統的調用方式
testClosure(10, 'groovy',{
println('I am a Closure')
})
//最後一個參數寫到圓括號外
testClosure(10, 'groovy') {
println('I am a Closure')
}
10. 文件IO
首先回顧一下在Java中進行文件IO,假設存在這樣的test.txt
hello
groovy
I
am
Jdqm.
現在需要通過Java IO讀這個文件的內容並輸出到控制檯
BufferedReader bufferedReader = null;
try {
File file = new File("test.txt");
//文本內容可以使用字符流
FileReader fileReader = new FileReader(file);
//將字符流包裝爲BufferReader,方便按行讀取
bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
即便使用Java7中的自動關閉流來省略掉finally塊,代碼仍然比較繁瑣。接下來使用Groovy來完成同樣的功能。
File file = new File('test.txt')
file.eachLine {online->
println online
}
到這裏我只想到兩個詞:簡潔、明瞭。這裏使用到了閉包,而閉包的難點是如何確定參數,最好的方式是查官方文檔。http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html#eachLine(groovy.lang.Closure)
從文檔說明中我們得知調用時閉包可以是1個或者2個參數, 第一個參數是當前行的內容,第二個參數(可選)是當前的行號(從1開始)。所以我們可以這樣
File file = new File('test.txt')
file.eachLine{line, lineNum ->
println lineNum + " " + line
}
//輸出結果
1 hello
2 groovy
3 I
4 am
5 Jdqm.
下面將test.txt的內容寫入test1.txt
File sourceFile = new File('test.txt')
File targetFile = new File('test1.txt')
targetFile.withOutputStream{os->
sourceFile.withInputStream{is->
os<<is
}
}
11. 解析XML
Groovy訪問xml有兩個類:XmlParser和XmlSlurper,二者幾乎一樣,在性能上有細微的差別,參考鏈接 http://docs.groovy-lang.org/docs/latest/html/api/。
def xml = '<root><one a1="uno!"/><two>Some text!</two></root>'
def rootNode = new XmlParser().parseText(xml)
assert rootNode.name() == 'root'
assert rootNode.one[0].@a1 == 'uno!'
assert rootNode.two.text() == 'Some text!'
rootNode.children().each { assert it.name() in ['one','two'] }
12.其他
在Groovy中,Getter/Setter和屬性是默認關聯的,比如:
class Book {
private String name
String getName() { return name }
void setName(String name) { this.name = name }
}
class Book {
String name
}
上述兩個類完全一致,只有有屬性就有Getter/Setter;同理,只要有Getter/Setter,那麼它就有隱含屬性。
with操作符
在Groovy中,當對同一個對象進行操作時,可以使用with,比如:
Book bk = new Book()
bk.id = 1
bk.name = "android art"
bk.press = "china press"
可以簡寫爲:
Book bk = new Book()
bk.with {
id = 1
name = "android art"
press = "china press"
}
判斷是否爲真
在Groovy中,判斷是否爲真可以更簡潔:
if (name != null && name.length > 0) {}
可以替換爲:
if (name) {}
簡潔的三元表達式
在Groovy中,三元表達式可以更加簡潔,比如:
def result = name != null ? name : "Unknown"
// 省略了name
def result = name ?: "Unknown"
簡潔的非空判斷
在Groovy中,非空判斷可以用?表達式,比如:
if (order != null) {
if (order.getCustomer() != null) {
if (order.getCustomer().getAddress() != null) {
System.out.println(order.getCustomer().getAddress());
}
}
}
可以簡寫爲:
println order?.customer?.address
使用斷言
在Groovy中,可以使用assert來設置斷言,當斷言的條件爲false時,程序將會拋出異常:
def check(String name) {
// name non-null and non-empty according to Gro ovy Truth
assert name
// safe navigation + Groovy Truth to check
assert name?.size() > 3
}
switch方法
在Groovy中,switch方法變得更加靈活,可以同時支持更多的參數類型:
def x = 1.23
def result = ""
switch (x) {
case "foo": result = "found foo"
// lets fall through
case "bar": result += "bar"
case [4, 5, 6, 'inList']: result = "list"
break
case 12..30: result = "range"
break
case Integer: result = "integer"
break
case Number: result = "number"
break
case { it > 3 }: result = "number > 3"
break
default: result = "default"
}
assert result == "number"
==和equals
在Groovy中,==相當於Java的equals,,如果需要比較對個對象是否是同一個,需要使用.is()。
Object a = new Object()
Object b = a.clone()
assert a == b
assert !a.is(b)