gradle学习

Groovy基础

常见语法

Groovy可以和Java一样声明和使用一个类,定义和使用一个方法。同时也提供了一个属性的默认的get和set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person {
private String name;
public Person(String name) {
this.name = name;
}
String getName() {
return name;
}
}
def person = new Person("xiuweikang");
print person.name
String getPersonName(Person person) {
return person.name
}
println getPersonName(person)
// 默认get、set
class Person {
String name;
}
def person = new Person()
person.setName("xiuweikang")
print person.getName()

字符串

Grooy存在两种字符串java.lang.Stringgroovy.lang.GString,其中单引号和三重单引号都是java.lang.String类型的,不支持占位符插值操作。双引号如果不包含占位符则是java.lang.String,如果有则是groovy.lang.GString。占位符可以用${}或者$来表示,前者主要用于一般替代字符串或者表达式,后者用于替代A.B这样形式的,但是无法对A.B()做出替换,只能通过${}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
String name;
}
def person = new Person()
person.setName("xiuweikang")
def name = "xiuweikang"
println person.name
println "his name is $name"
println "his name is ${name}"
println "his name is $person.name"
println "his name is ${person.name}"
println "his name is ${person.getName()}"
println "his name is $persion.getName()"// wrong

容器

Range
1
2
3
4
5
6
7
def x = 1..10
println x
println x.contains(5)
println x.size()
println x.from
println x.to
println x.reverse()
List
1
2
3
4
5
def list = [1,2,3]
assert list instanceof java.util.ArrayList
def alist = [1,2,3]as LinkedList
assert alist instanceof java.util.LinkedList
Map
1
2
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
println colors['red']

流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def list = [1,2,2,3,4]
for(int i = 0;i < list.size();i++) {
println i
}
for(i in list) {
println i
}
switch(1) {
case 1:
println 1
break
case 2:
break;
default:
break;
}
def a = 0;
while(a++ < 10) {
println a
}

闭包(Closure)

闭包是一个被包起来的代码块对象,它的行为有点像方法,可以传参数进去然后返回一个值,它也是一个普通的对象,你可以对它持有一个引用。也可以用作方法参数的代码块,Groovy的闭包更象是一个代码块或者方法指针,代码在某处被定义然后在其后的调用处执行。

声明

{[closureParameters ->] statements}

前面的closureParameters是参数列表,类似于方法的参数列表。后面的则是代码块了。默认是存在一个参数it。如果你想声明一个不接受任何参数的闭包,且必须限定为没有参数的调用,那么你必须将它声明为一个空的参数列表。

闭包的调用方式有两种:

  • 闭包对象.call(参数)
  • 闭包对象(参数)
1
2
3
4
{item++}
{-> print it} //隐含参数it
{it -> print it}//使用明确的参数it
{String x, int y -> print x, print y}
  • 当看到{}的时候等同于:new Closure(){}
  • closure总是有返回值的,如果没有写return,则返回最后一行代码的左值,如果没有值,则是null
  • 当闭包作为闭包或方法的最后一个参数时我们可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数(方法调用时都可以省略括号),则方法参数所在的圆括号也可以省略;对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def debugClosure(int num, String str,Closure closure){
closure()
}
debugClosure(1, "groovy"){
println"hello groovy!"
}
def debugClosure(Closure closure){
closure()
}
debugClosure(){
println"hello groovy!"
}
debugClosure{
println"hello groovy!"
}

Groovy支持.&方法指针操作符,因为闭包可以被作为一个方法的参数,如果想让一个方法作为另一个方法的参数则可以将一个方法当成一个闭包作为另一个方法的参数。如下:

1
2
3
4
5
6
7
8
class A{
int add(int x, int y) {
return x + y
}
}
A a = new A();
Closure cAdd = a.&add;
print cAdd(1,2)

Closure delegate

每个闭包都有一个代理对象,在闭包上未找到的属性和方法都会转给代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyDelegate {
def func() {
println 'func'
}
}
Closure c = {
func();
}
c.delegate = new MyDelegate()
c.call()

例如在build.gradle中,常会使用dependencies语句块。
// build.gradle

dependencies {
compile ‘xxx:xxx:1.0’
testCompile ‘xxx:xxx:1.0’
}
dependencies是Project对象中定义的DSL方法,后面的大括号是其接受的闭包参数;这里Groovy的括号可以省略;
传进来的闭包的delegate被设置成DependencyHandler,因此当其调用compile方法时,内部其实是调用的DependencyHandler中的compile/testCompile方法。

Gradle

gradle基础

在Gradle中每个待编译的工程都是一个Project,每一个Project在构建的时候都包含一系列的Task,比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等,这些Task大部分是由插件定义的,而Gradle则只是一个框架,它只负责定义流程和规则。而相应的工作都由相应的插件支持,比如编译Java的有相应的Java插件,Andrid的有Android插件。

Android中的每一个Library和每一个App都是单独的一个Project,而每个Project下都会存在一个build.gradle文件(该文件名是默认的,当gradle执行命令时会在当前目录下找到build.gradle文件),这个文件就是该Project的编译脚本。当一个目录下存在多个Project,我们构建时,需要cd到每个Project下然后执行相应的grdle命令,如果我想在根目录下直接执行gradle命令就让所有的Project都build的话就需要在根目录下添加setting.gradle,它来告诉gradle这个目录下存在多少个Project,然后都执行每个Project的build.gradle,同时根目录下也可以有build.gradle文件,它可以配置其他子Project,比如为子Project添加一些共通的属性等。

gradle命令

gradle projects 查看一个multi Projects下面包含多少个子Project
14:34:48.jpg
相应的settings.gradle include ':app', ':aaa', ':mylibrary'

gradle tasks 查看task信息,也可以gradle project-path:tasks查看某个Projects下的tasks
14:39:09.jpg
14:39:47.jpg
14:41:04.jpg

gradle task-name执行相应的task
比如在某个project下写一个task

1
2
3
4
5
task hello {
doLast {
println 'Hello world!'
}
}

gradle hello 则会执行hello task。上面的图包括了好多的Task,比如:

  • gradle clean是执行清理任务,和make clean类似。
  • gradle properites用来查看所有属性信息。

注意:Task之间是存在依赖的,比如TaskA依赖TaskB则在执行TaskA时,TaskB首先需要被执行。

gradle工作流程


Gradle分为三个阶段:

  • 初始化阶段(Initiliazation): 对multi-project而言就是执行setting.gradle,在构建时会为当前项目创建一个Settings类型的实例,如果当前项目存在settings.gradle文件,则通过该文件配置Settings实例。
  • 配置阶段(Configuration): 在这个阶段会解析每个project的build.gradle,它会建立一个有向图来描述Task之间的依赖关系。
  • 执行阶段(Execution):在这个阶段会对gradle命令指定的task及其依赖task进行执行.

最后,关于Gradle的工作流程,你只要记住:

  • Gradle有一个初始化流程,这个时候settings.gradle会执行。
  • 在配置阶段,每个Project都会被解析,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系。
  • 然后才是执行阶段。你在gradle xxx中指定什么任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍!

gradle实例详解

Gradle总共有三种对象,分别对应三种不同的脚本文件,三种对象为gradle脚本的代理对象(delegate object),我们可以在脚本中使用代理对象的属性和方法:

  • Gradle对象:当我们执行gradle …的时候,gradle会从默认的脚本中构造出一个Gradle对象,主要用于为接下来的构造工作做一些准备工作。如果要写这种初始化脚本应该按照规则放在下面的目录下,我们一般很少去定制这个脚本:
    19:10:54.jpg
  • Project对象:每一个build.gradle对象都会最终转换成一个Project对象。
  • Settingds对象:每一个settings.gradle对象最终都会转换成Settings对象。

具体流程:

  1. 为当前项目创建一个Settings类型的实例。
  2. 如果当前项目存在settings.gradle文件,则通过该文件配置刚才创建的Settings实例。
  3. 通过Settings实例的配置创建项目层级结构的Project对象实例。
  4. 最后通过上面创建的项目层级结构Project对象实例去执行每个Project对应的build.gradle脚本。

Gradle对象

Gradle对象在整个执行过程中只存在一个,同时包含了很多的属性和方法,我们都可以在脚本文件中直接调用,比如:

在根目录下的build.gradle和setting.gradle中设置下面的,可以看到gradle对象是同一个

1
2
3
4
5
////在settings.gradle中,则输出"In settings,gradle id is"
println "In gradle, gradle id is " +gradle.hashCode()
println "Home Dir:" + gradle.gradleHomeDir
println "User Home Dir:" + gradle.gradleUserHomeDir
println "Parent: " + gradle.parent

Project对象

加载插件

Project对象中包含自己的属性和方法,同时每个Project会包含很多Task,而这些Task往往是由插件决定的:

加载插件可以通过Project中定义的apply方法,参数是一个map,key包含三种,如下图:

1
2
3
4
apply plugin: 'com.android.library' <==如果是编译Library,则加载此插件
apply plugin: 'com.android.application' <==如果是编译Android APP,则加载此插件
//也可以加载某个目录下的gradle文件,比如加载一个共通方法的gradle
apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
设置属性

有些时候我们需要配置一些共通的属性方便在不同的Project中调用,这样在之后的更改中只更改配置文件中的共通属性就可以了,不需要每个Project都改。
Gradle提供了一种名为extra property的方法。extra property是额外属性的意思,在第一次定义该属性的时候需要通过ext前缀来标示它是一个额外的属性。定义好之后,后面的存取就不需要ext前缀了。ext属性支持Project和Gradle对象。即Project和Gradle对象都可以设置ext属性

举个例子:在setting.gradle中设置一些属性,在其他project时取到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def getSDKPath(){
//属性值从local.properites中读取
Properties properties = new Properties()
File propertyFile = new File(rootDir.getAbsolutePath() +"/local.properties")
properties.load(propertyFile.newDataInputStream())
//gradle就是gradle对象。它默认是Settings和Project的成员变量。可直接获取
//ext前缀,表明操作的是外置属性。api是一个新的属性名。前面说过,只在
//第一次定义或者设置它的时候需要ext前缀
gradle.ext.sdkDir =properties.getProperty('sdk.dir')
println gradle.sdkDir //再次存取api的时候,就不需要ext前缀了
}
//可以在其他的project中调用,因为整个执行过程中的gradle对象是同一个。
task projectTask <<{
println gradle.sdkDir
}