学习Gradle,前前后后总结了一些内容,然后整理出了一个系列,共计10篇文章,与大家分享:
- Groovy基本介绍
- 开始使用Gradle
- 自定义属性
- 深入了解Task和构建生命周期
- Gradle增量式构建
- 挂接到构建生命周期
- 编写和使用自定义Task
- 依赖管理
- 多项目构建
- 自定义Gradle插件
学习本系列前可以下载相关的github项目gradleLearnDemo。
地址:https://github.com/sososeen09/gradleLearnDemo
0 前言
Gradle脚本使用的是Groovy语言,Groovy也是基于JVM的一种动态语言,基于Java并扩展了Java语言。Groovy会使写Java程序就要写脚本一样简单,写完就可以执行,所以看起来也像是一门脚本语言。Groovy内部会把代码编译为Java class文件然后启动虚拟机来执行,这些过程,我们开发人员是不用管的。
Gradle是一种DSL,也即是 Domain Specific Language 领域特定语言。说白了DSL就是一种行话,这个语言在Gradle中可以识别,在其它地方就用不了了。
学习Gradle,我们不必要精通Groovy语言,但还是需要了解一些Groovy的基本知识,比如def关键字、返回语句、字符串、集合、闭包等。下面,我们就来简单介绍一下。
1 Groovy代码的执行
前面我们说了,Groovy很像是一门脚本语言,我们来看一下怎么回事。
创建一个HelloGroovy.groovy文件,里面就一行代码
println 'hello groovy!'
然后执行命令groovy HelloGroovy.groovy
,得到结果
hello groovy!
是不是很简单,很像脚本语言。
2 Groovy中的字符串
Groovy 对字符串支持相当强大,可以使用多种字符串,包括单引号、双引号、三引号。
String str='hello groovy!'
String getStr(){
'hello groovy!'
}
String str1='hello'
String str2=' groovy!'
println 'hello groovy!'
println "hello groovy!"
println "$str"
println getStr()
println "${str1+str2}"
println 'hello $str2' //单引号,直接打印内容
使用单引号会直接打印字符串的内容,不会对美元$符号的内容进行转义。使用双引号的功能更加强大,字符串中使用美元符$后面可以跟字符串变量,如"$str"
,也可以跟表达式如 "${str1+str2}"
,如果跟表达式,记得要用{}括起来。
三引号用的比较少,可以支持字符串内容换行,我们了解一下就行。
println ''' aa
bb
cc
'''
我们在getStr()方法中并没有使用return语句,这是因为Groovy默认是把最后一行的执行结果进行返回。如果你需要返回的结果正好是最后一行,就可以省略return。
另外,在Groovy中,语句的最后的“;”
号是可以省略的。还有一点就是Groovy中在调用函数的时候可以不加括号,比如,println ("hello")
与 println "hello"
的意思是一样的
3 def关键字
def关键字很像是Java中的Object,在定义变量或者方法的时候使用def,如果没有指定具体的参数类型。在运行阶段Groovy会自动判断参数类型。
def a = 1, b = 2
def str = "hello"
println a + b
println str
实际上我们在定义变量的时候是可以省略def的,如
a = 1
b = 2
str = "hello"
但是,最好还是加上def,这是一种好的实践。
你还可以在定义变量的时候,在def后面再加上具体的参数类型,如
def int a = 1
def String str = "hello"
使用def定义方法的返回值类型,可以返回任意类型。如果指定了具体的返回值类型,就要正确的返回,否则会报错。
4 集合
Groovy的基本类型与Java一样。对于集合类型的数据,List的具体实现是ArrayList。Map集合的具体实现是LinkedHashMap。
4.1 List
下面我们来简单了解一下如果使用。List使用一个 []中括号来括起来来表示的。
//定义一个List
def lists=['groovy','gradle','android']
//打印集合size
println lists.size
//打印集合中的数据
lists.each{
list->println list
}
取出集合中的数据直接用索引就可以了。比如:
assert lists[0]=='groovy'
对于集合中变量的存储,我们是不需要担心数组越界的,如果索引超过当前长度,List会自动在该索引中添加元素。比如
lists[100]=100
println lists.size
println lists[99]
打印结果,会发现集合的size变为101。index为99的集合数据为null。
4.2 Map
Map的表示就是用中括号[]括起来的 key:value形式。
def map=["key1":"hello","key2":"groovy","key3":true]
println map.size()
map.forEach{
key,value->
println "$key :$value"
}
结果是:
3
key1 :hello
key2 :groovy
key3 :true
获取Map的元素
println map.'key1'
println map['key1']
添加元素
map.'key4'='android'
println map['key4']
5 Groovy的类
Groovy中的类与Java中的类的写法类似,但是要更简洁一些。
我们在Hello.groovy文件中写一个HelloWorld类。
class HelloWorld{
String msg='hello world'
HelloWorld(){
}
HelloWorld(String msg){
this.msg=msg
}
void sayHello(){
println msg
}
}
def say={
new HelloWorld('hello').sayHello()
}
def getMsg={
new HelloWorld().msg
}
say()
println getMsg()
调用groovy Hello.groovy
命令执行该脚本,结果如下:
hello
hello world
当然了,我们也可以写一个单独的HelloWorld类,带上包名,然后在其它地方导包使用,就像Java那样。有关于Groovy类的使用,我们后面还会讲到,这里就不赘述了。
总结一下Groovy的类相比Java的一些不同,或者说是优化的一些地方:
- 表达式后面的分号是可选的
- 每个类、构造方法和方法默认是public的
- 在Groovy中,方法体中的最后一个表达式的值会被作为返回值。这意味着return语句是可选的
- Groovy编译器自动加上getter/setter方法,所以不需要自己去书写
- 类的属性可以通过点号来获取,看起来好像它们在Java中是public的,在底层Groovy调用的是自动生成的getter/setter方法。
6 闭包
闭包Closure,在Groovy中是很重要的一种数据类型。闭包实际上就是一段代码块,需要用{}包括起来。前面我们在讲解一些例子的时候已经用到了闭包,我们再来看一下闭包的结构。
//定义一个闭包 ,记得要用{}包裹起来
def aClosure={
int a,int b-> //-> 箭头前面代表的是参数,后面是执行语句
a+b //返回值
}
println aClosure(1,2)
结果为 3。
闭包的参数类型也可以不指定,在运行期有Groovy自动推断,比如下面这个例子,执行起来也是没有问题的
def bClosure={
a,b->
a+b
}
println bClosure(1,2)
闭包也可以没有参数
如:
def aa={
println "this is a Closure"
}
aa()
总结一下,闭包的类型有
1. def closureNama ={params -> code}
2. def closureName= {code} 没有参数的时候就没有箭头 ->
闭包的调用有两种方式
1. 闭包对象.call(参数)
2. 闭包对象(参数)
如
aClosure(1,2)
aClosure.call(1,2)
需要注意一点,闭包如果没有参数的话,其隐含了一个参数是 it
。
和this的作用类似,代表的是传入闭包的参数。比如:
def sayHello={"hello ${it}"}
println sayHello("Jim")
等同于
def sayHello={it->"hello ${it}"}
当然了,如果闭包显示的指明了无参数,则在调用闭包的时候不能传参数,否则会报错。如
def noParams={->println "noParams"}
noParams();
//下面的代码执行会报错
//noParams(1)
闭包返回值
闭包总是会返回一个值。返回值是闭包的最后一条语句的值(如果没有显式的return语句),或者是可执行的return 语句的值。如果闭包的最后一条语句没有值就返回null。如之前举得Hello.groovy中的例子:
def getMsg={
new HelloWorld().msg
}
闭包作为方法参数
闭包也可以作为方法参数,如:
int increment(Closure closure,int count){
closure()+count
}
//断言,如果为true就正常执行,如果为false,就会报错
assert increment({1+1},1)==3
闭包委托
闭包代码是在委托的闭包上执行。默认的,这个委托就是闭包的所有者。比如你在Groovy脚本中定义了一个闭包,那么所有者就是一个groovy.lang.Script实例。闭包的隐式变量delegate 允许你重新定义默认的所有者。
例如:
class Test {
def x = 30
def y = 40
def run() {
def data = [ x: 10, y: 20 ]
def cl = {
y = x + y
}
cl.delegate = data
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
assert x == 30
assert y == 40
assert data == [x:10, y:30]
}
}
new Test().run()
上面例子中闭包c1的委托变为data,闭包的resolveStrategy在默认情况下是OWNER_FIRST,即它会先查找闭包的owner(在本例中指的就是Test对象本身),如果owner存在,则在owner上执行闭包中的代码。这里我们将其设置成了DELEGATE_FIRST,即该闭包会首先查找delegate(本例中即data),如果找不到再去找owner。resolveStrategy还有其它的一些情况,具体的可以查看文档中的例子,相信当你看到这些例子后,会对闭包委托有一个清晰的认识。
7 总结
限于篇幅和本系列的主题,本文简单介绍了Groovy的一些语法和数据结构,这对于学习Gradle会有一些帮助。在后面Gradle的学习过程中,如果对Groovy的一些Api不熟悉可以查看Groovy的Api文档,我们没必要死记硬背这个Api,掌握学习的方法更加重要,对吗?
下一篇,我们就正式进入Gradle部分的学习了。