Gradle详解(一)——Groovy语法快速入门

1 概述

Groovy是一种运行于Java虚拟机的语言。当编译执行Groovy代码时,Groovy代码会被先编译成Java字节码,然后再在Java虚拟机中执行。

2 基本语法

注释

Groovy支持单行注释(//)和多行注释(/*内容*/)。

Groovy语句末尾不用加分号。

定义变量

Groovy支持动态类型,即定义变量的时候可以不指定其类型。Groovy中,定义变量使用关键字def(def关键字也可以省略)。

def variable1 = 1 //不指定类型,使用def定义变量
def variable2 = "I am a person"

variable3 = "hello" //不指定类型,也不写def

byte b = 1 //变量定义时,也可以直接指定类型
char c = 2
short s = 3
int i = 4
long l = 5
float f = 1.234
double d = 2.345
boolean untypedBooleanVar = false

定义、调用函数

定义函数时,可以不指定参数的类型:

String testFunction(arg1,arg2){//无需指定参数类型
	...
}

也可以不指定函数的返回值类型。但若不指定函数的返回值类型,则必须加上def关键字:

String getString(){
	"I am a string"//最后一行代码的值就是本函数的返回值
}

//不指定返回值类型,则必须使用def关键字
def nonReturnTypeFunc(){
	"I am a string"//最后一行代码的值就是本函数的返回值
}

Groovy的函数里,可以不使用 return xxx 来设置 xxx 为函数返回值。如果不使用 return 语句的话,则函数中最后一行代码的执行结果被设置成返回值。

Groovy中调用函数时可以省略小括号,如println("hello")println "hello"是等价的。

字符串

  • 单引号字符串:单引号字符串等价于Java中的字符串。不支持$表达式。
def age = 20
def str = 'I am $age years old'

println str //打印结果为:I am $age years old
  • 双引号字符串:双引号字符串支持 K o t l i n 表达式。和Kotlin中的情形一样, 表达式有两种格式,一种是 后跟一个简单的名字,如` name;另一种是$后跟一个花括号,花括号中可以是一个复杂的表达式,如 l i s t . s i z e ( ) {list.size()}`、` {2+6}`等。
def age = 20
def str = "I am $age years old"

println str //打印结果为:I am 20 years old
  • 三单引号字符串:三单引号字符串支持跨行。不支持转义,任何字符都会被原样展示(包括换行、空格、tab等)。
def str = '''line one
line two
line three
'''

3 容器类

3.1 List

使用[]来进行定义:
def aList = [5,'string',true] //元素的类型可以不同

通过index访问元素,不用担心index越界:
assert aList[1] == 'string'
assert aList[5] == null //第6个元素为空
aList[100] = 100 //设置第101个元素的值为10
println aList.size //结果是101

3.2 Map

定义:
//冒号左边是key,右边是value。key必须是字符串,value可以是任何类型。key可以用''或""包起来,也可以不用引号包起来。
def aMap = ['key1':'value1','key2':true]

访问:
println aMap.key1
或
println aMap['key1']

3.3 Range

//包含1,2,3,4,5这5个值
def aRange = 1..5

//只包含1,2,3,4这4个元素
def aRangeWithoutEnd = 1..<5

//迭代
def sum = 0
aRange.each{element->
    sum+=element
}

4 闭包

闭包(Closure)是Java所不具备的语法结构(JAVA8增加了对闭包的支持)。闭包就是一个代码块,用“{ }”包起来。此时,程序代码也就成了数据,可以被一个变量所引用。

闭包的创建过程很简单,例如:

{ 参数 ->
	代码...
}

参考下面的示例代码,定义了c1和c2两个闭包,并对它们进行调用:

定义闭包:
def c1 = { text -> println text }
def c2 = { println it }//如果闭包只有一个参数,则参数和->都可以不写,在后面的代码中以it代表调用时传入的该参数

调用闭包:
c1.call("content1") //方式1,用call方法调用闭包
c2("content2") //方式2,直接调用闭包

闭包的返回值和函数的返回值定义方式是一样的:如果有return语句,则返回值是return语句后面的内容;如果没有return语句,则闭包内的最后一行代码就是它的返回值。

类比c语言,指向闭包的变量就像是一个函数指针,而闭包就是一个函数。

省略参数圆括号

闭包在Groovy中大量使用,比如很多函数的最后一个参数都是一个闭包。比如:

def debugClosure(int num, String str, Closure closure){
	//dosomething  
}

//调用函数
debugClosure(1, "groovy", {  
	println"hello groovy!"  
})

当闭包作为函数的最后一个参数时,我们可以将闭包从参数圆括号中提取出来接在最后:

debugClosure(1, "groovy"){  
	println"hello groovy!"  
}

也可以直接省略圆括号:

debugClosure 1, "groovy", {  
	println"hello groovy!"  
}

由以上知识可知,在build.gradle文件中,形如:

defaultConfig {
	applicationId "com.dmall.pop"
	minSdkVersion 15
	targetSdkVersion 22
	versionCode 4
	versionName "2.1.0"
	vectorDrawables.useSupportLibrary = true
}

或

variant.outputs.each { output ->
	def oldFile = output.outputFile
	def gitTag = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
	if (variant.buildType.name.equals('release')) {
		def releaseApkName = 'POP_' + variant.productFlavors[0].name + '_v' + defaultConfig.versionName + '_' + gitTag + '.apk'
		output.outputFile = new File(oldFile.parent, releaseApkName)
	}
}

其实都是一个方法调用,方法名是defaultConfig和each,方法名后跟着的闭包是传递给方法的参数(省略了参数外面的小括号)。

一个闭包可以理解为一个方法,将一个闭包作为参数传递给另一个方法,其实类似于c语言中的将函数指针作为参数传递给另一个函数。类比android,这里闭包其实很像android的liseter,我们将一个闭包传递过去,在未来的某个时间,这个闭包就会被调用。

5 将一般的函数当做闭包使用

Groovy支持.&函数指针运算符,因为闭包可以被作为一个函数的参数,如果想让一个函数作为另一个函数的参数,则可以通过.&运算符将一个函数当成一个闭包作为另一个函数的参数。

def list = ['a','b','c']
//常规写法
list.each{
    println it
}

String printName(name){
    println name
}

//方法指针操作符写法
list.each(this.&printName)

6 变量的作用域

在groovy文件(xxx.groovy)中可以像在java文件中那样定义一个类,也可以直接写脚本(即直接写要执行的操作),例如test.groovy的代码是:

println 'Groovy world!

它会被转换成这样的Java类:

  • test.groovy被转换成了一个test类,它继承自Script。
  • Java类中会有一个main函数,当我们执行脚本的时候,实际就是去执行main函数。
  • 脚本中的所有代码都会放到run函数中。也就是说,println 'Groovy world'这句代码实际上是包含在run函数里的。
  • 脚本中定义的函数,会被转换成test类的成员函数。
  • 脚本中以def 变量名类型 变量名格式定义的变量,会被转换为run方法中的局部变量。

再来看另一个脚本文件:

def x = 1 //注意,这个x有def(或者指明类型,比如 int x = 1)
def printx(){
	println x
}

printx() //报错,说 x 找不到

报错的原因很明显——成员函数printx无法访问到run函数中的局部变量x。

那么要想在printx函数中访问变量x,该如何做呢?——只要去掉x前的def或者类型即可,即写成x = 1。此时的Java代码如下图所示:

可见,x仍没有被定义成test的成员变量,而是在run的执行过程中,将x作为一个属性添加到test实例对象中了,然后在printx函数中,先获取这个属性。

虽然printx可以访问变量x了,但是在其他脚本文件中,依然无法访问变量x,例如我们在test.groovy同目录下创建了一个test1.groovy:

def atest = new test()
atest.printx()

提示找不到x,这是因为x是在test.groovy的run函数中动态地添加到test实例中去的。那该怎么办呢?只要给x加上@Field注解:

import groovy.transform.Field; 

@Field x = 1 //在x前面加上@Field注解,这样,x就是test的成员变量了

7 文件I/O

相关API文档:

API
java.io.File http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
java.io.InputStream http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
java.io.OutputStream http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
java.io.Reader http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
java.io.Writer http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
java.nio.file.Path http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html

7.1 读文件

def targetFile = new File(文件名)

//读取文件的每一行并打印
targetFile.eachLine{
	String oneLine ->
	println oneLine
}

//一次性读取文件内容,得到一个bytes[]
targetFile.getBytes()

//获得读入流
def is = targetFile.newInputStream()
//关闭流
is.close

//使用闭包操作inputStream,常用。
targetFile.withInputStream{ is ->
	//操作is。不用close。Groovy会自动替你close
}

7.2 写文件

举个copy文件的例子:

def srcFile = new File(源文件名)
def targetFile = new File(目标文件名)

targetFile.withOutputStream{ os->
	srcFile.withInputStream{ is->
		//利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream的输出
		os << is 
	}
}

7.3 解析XML

test.xml文件:

<response version-api="2.0">
	<value>
		<books>
			<book available="20" id="1">
				<title>Don Xijote</title>
				<author id="1">Manuel De Cervantes</author>
			</book>
			<book available="14" id="2">
				<title>Catcher in the Rye</title>
				<author id="2">JD Salinger</author>
			</book>
			<book available="13" id="3">
				<title>Alice in Wonderland</title>
				<author id="3">Lewis Carroll</author>
			</book>
			<book available="5" id="4">
				<title>Don Xijote</title>
				<author id="4">Manuel De Cervantes</author>
			</book>
		</books>
	</value>
</response>

解析:

def xparser = new XmlSlurper()
def targetFile = new File("test.xml")
GPathResult gpathResult =xparser.parse(targetFile)
 
//访问id=4的book元素
//gpathResult代表根元素response。通过e1.e2.e3这种格式就能访问到各级子元素....
def book4 = gpathResult.value.books.book[3]

//得到book4的author元素
def author = book4.author

//获取元素中的文本
assert author.text() == 'Manuel De Cervantes'

//获取属性。格式:author.@属性名 或 author['@属性名']。属性一般是字符串,可通过toInteger转换成整数
[email protected]() == 4

8 DSL

DSL即领域特定语言。Groovy是通用的编程语言,所以不是DSL,但是使用Groovy可以很容易实现DSL,这主要是因为Groovy语言的一些特性,如:

  • Groovy源文件中不需要定义Class,可以直接写代码;
  • Groovy调用函数时可以省略函数参数的小括号;
  • 等。

9 参考

深入理解Android之Gradle:http://blog.csdn.net/innost/article/details/48228651

Groovy脚本基础全攻略 :http://blog.csdn.net/yanbober/article/details/49047515

发布了46 篇原创文章 · 获赞 38 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/al4fun/article/details/78385980