一、Gradle 基础
二、Gradle 构建
三、Task 编写和使用
四、Plugin 编写和使用
Gradle 基础
-
Projects
:像应用或者库一样,一个Gradle
项目是一个可以编译的软件。单个项目本身是根目录,如果多个项目时,那么应该由一个根项目和多个子项目构成。 -
Build Scripts
:构建加脚本详细记录Gradle
如何完成一个项目的构建。每个项目至少包含一个构建的脚本。 -
Dependency Management
:依赖管理可以自动解析项目声明好的外部资源依赖。 -
Tasks
:任务是编译代码或者运行测试的基本工作单元。在一个插件或者构建脚本中,每个项目至少需要执行一个任务。 -
Plugins
:插件可以用于扩展Gradle
的功能,并可选择性地为项目增加额外的任务。
1.1 Gradle 的基础概念
1.1.1 Distribution
Distribution,即Gradle
发布版本。每个Gradle
版本发布时,一般会有三个项目,分别是src
(源码)、bin
(精简可运行包)和all
(不仅包含可运行文件,还包含sample
以及用户文档)。
Gradle
是基于JVM
的运行程序,本质上是一个JVM
程序,通过bin
下文件,运行JVM
程序,并加载lib
下所需的jar
包。
1.1.2 Wrapper
因为Gradle
版本较多,而且Gradle
文件比较大,如果随项目一起,不是很方便。这时,就需要wrapper,wrapper
是一个脚本,它会通过一些配置项和执行文件,在实际构建项目时,首先会检查本地是否有符合要求的gradle distribution
,如果没有,则会下载,如果已经存在,则直接构建项目。这样,开发人员可以快速启动并运行 Gradle
项目。
1.1.2.1 添加 wrapper
执行$ gradle wrapper
命令可以添加wrapper
,添加wrapper
后,文件目录如下:
├── a-subproject
│ └── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
gradle-wrapper.jar
:下载Gradle distribution
的wrapper jar
文件;gradle-wrapper.properties
:配置wrapper
属性的属性文件,比如:graddle distribution
版本、是否使用代理、Gradle distribution
路径等;gradlew, gradlew.bat
:执行wrapper
构建的shell
以及bat
脚本;
哪怕没有gradle
的运行环境,你也可以执行wrapper
来完成构建的工作。
1.1.2.2 使用 wrapper
$ gradlew.bat build
Downloading httpsservices.gradle.orgdistributionsgradle-5.0-all.zip
.....................................................................................
Unzipping CDocuments and SettingsClaudia.gradlewrapperdistsgradle-5.0-allac27o8rbd0ic8ih41or9l32mvgradle-5.0-all.zip to CDocuments and SettingsClaudia.gradlewrapperdistsgradle-5.0-alac27o8rbd0ic8ih41or9l32mv
Set executable permissions for CDocuments and SettingsClaudia.gradlewrapperdistsgradle-5.0-allac27o8rbd0ic8ih41or9l32mvgradle-5.0bingradle
BUILD SUCCESSFUL in 12s
1 actionable task 1 executed
如果本地没有gradle
运行环境,那么Wrapper
会下载distribution
到本地文件系统。以后,只要distribution url
没有改变,那么distribution
可以重复使用。
1.1.2.3 升级wrapper
- 修改
gradle-wrapper.properties
中distributionUrl
属性; - 执行命令,比如:
.gradlew wrapper --gradle-version latest
或者.gradlew wrapper --gradle-version 8.5
1.1.3 Gradle User Home directory 和 Project Root directory
Gradle
主要依靠Gradle User Home
和 Project Root
两个文件夹管理以及展开工作。
1.1.3.1 Gradle User Home directory
默认情况下,Gradle User Home
存储位置位于:~.gradle
或CUsersUSERNAME.gradle
,文件夹内保存全局的配置文件、初始化脚本、缓存以及日志文件。如果想要修改路径,可以通过设置环境变量GRADLE_USER_HOME
修改。
其文件夹结构大体如下:
├── caches ①
│ ├── 4.8 ②
│ ├── 4.9 ②
│ ├── ⋮
│ ├── jars-3 ③
│ └── modules-2 ③
├── daemon ④
│ ├── ⋮
│ ├── 4.8
│ └── 4.9
├── init.d ⑤
│ └── my-setup.gradle
├── jdks ⑥
│ ├── ⋮
│ └── jdk-14.0.2+12
├── wrapper
│ └── dists ⑦
│ ├── ⋮
│ ├── gradle-4.8-bin
│ ├── gradle-4.9-all
│ └── gradle-4.9-bin
└── gradle.properties ⑧
- 全局缓存目录(不包含项目特有的缓存);
- 不同版本缓存目录(比如支持增量编译);
- 共享的缓存;
Gradle Daemon
注册表和日志;- 全局初始化脚本;
- 工具链支持下载的
JDK
; Gradle Wrapper
下载的Distributions
;- 全局配置文件;
1.1.3.2 Project Root directory
Project Root
包含项目所有源文件的同时,还包含像.gradle
和build
等gradle
创建的文件和文件夹。
其文件夹结构大体如下:
├── .gradle Ⅰ
│ ├── 4.8 Ⅱ
│ ├── 4.9 Ⅱ
│ └── ⋮
├── build Ⅲ
├── gradle
│ └── wrapper Ⅳ
├── gradle.properties Ⅴ
├── gradlew Ⅵ
├── gradlew.bat Ⅵ
├── settings.gradle.kts Ⅶ
├── subproject-one Ⅷ
└── build.gradle.kts Ⅸ
├── subproject-two Ⅷ
└── build.gradle.kts Ⅸ
└── ⋮
Gradle
创建的项目特有缓存文件夹;- 版本专有缓存(比如支持增量编译);
- 项目中
Gradle
创建的编译目录; - 保存
Gradle Wrapper
的JAR
文件以及配置文件; - 项目特有的配置文件;
- 使用
Wrapper
编译的脚本; - 定义子项目的项目设置文件;
- 一般情况下,一个或多个子项目组成一个完整的项目;
- 每个子项目自己的
Gradle
编译脚本;
1.1.4 Client
client
进程是个轻量级进程,每次构建开始都会创建这个进程,构建结束会销毁这个进程。
1.1.5 Daemon
Daemon
不可以直接与用户交互控制,而是作为后台程序运行。Gradle
使用JVM
,JVM
运行需要加载一些必要的支持库。这些库加载需要花费大量时间,所以,JVM
的启动通常比较缓慢,针对该问题,提出Gradle Daemon
概念解决该问题。Daemon
常驻于后台,之所以可以有效加快构建的速率,主要是因为:
- 缓存构建过程中项目的信息
- 在后台运行,避免每个
Gradle
构建重新启动JVM
- 可以从
JVM
持续性运行优化获益 - 监控文件系统,省略重新构建时一些重复构建工作
Gradle Dameon
和Gradle Client
之间通过Socket
通信
1.2 Groovy 语法基础
Groovy
是⼀种基于 JVM
的动态语⾔,他的语法和 Java
相似,最终也是要编译 .class
在 JVM
上运⾏。Groovy
完全兼容 Java
并且在此基础上添加了很多动态类型和灵活的特性,⽐如⽀持闭包,⽀持DSL
(领域特 定语⾔),是⼀⻔⾮常灵活的动态脚本语⾔。
1.2.1 变量
Groovy
中⽤def
关键字来定义变量,可以不指定变量的类型,默认访问修饰符是public
。
def a = 1;
def int b = 1;
def c = "hello world";
1.2.2 数据类型
Groovy
中的数据类型主要有以下⼏种:
Java
中的基本数据类型Groovy
中的容器类- 闭包
1.2.2.1 字符串
在Groovy
种有两种字符串类 型,普通字符串String
(java.lang.String
)和插值字符串GString
(groovy.lang.GString
)。
在Groovy
中单引号字符串和双引号字符串都可以定义⼀个字符串常量,只不过单引号字符串不⽀持插值。要想插值可以使⽤双引号字符串,插值指的是替换字符串中的占位符,占位符表达式为 ${}
或 者以 $
为前缀。
def name = 'Android进阶之光'
println "hello ${name}"
println "hello $name"
三引号字符串可以保留⽂本的换⾏和缩进格式,不⽀持插值。
def name = '''Android进阶之光
Android进阶解密
Android进阶?'''
println name
1.2.2.2 List
Groovy
没有定义⾃⼰的集合类,它在Java
集合类的基础上进⾏了增强和简化。Groovy
的List
对应Java
中的List
接⼝,默认的实现类为Java
中的ArrayList
。
def number = [1, 2, 3]
assert number instanceof List
def linkedList = [1, 2, 3] as LinkedList
assert linkedList instanceof java.util.LinkedList
可以使⽤as
操作符来显式指定List
的实现类为java.util.LinkedList
。 获取元素同样要⽐Java
要简洁些,使⽤ []
来获取List
中具有正索引或负索引的元素。
def number = [1, 2, 3, 4]
assert number [1] == 2
assert number [-1] == 4 // -1 列表最后一个元素
number << 5 // 2 列表末尾追加⼀个元素
assert number [4] == 5
assert number [-1] == 5
1.2.2.3 Map
创建Map
同样使⽤ []
,需要同时指定键和值,默认的实现类为java.util.LinkedHashMap
。
def name = [one: '魏⽆羡', two: '杨影枫', three: '张⽆忌']
assert name['one'] == '魏⽆羡'
assert name.two == '杨影枫'
1.2.3 流程控制语句
1.2.3.1 for 循环
Groovy
⽀持Java
的for(int i=0;i<N;i++)
和for(int i:array)
形式的循环语句,另外还支持for in loop
形式,支持遍历范围、列表、Map
、数组和字符串等多种类型。
// 遍历范围
def x = 0
for ( i in 0..3 ) {
x += i
}
assert x == 6
// 遍历列表
def x = 0
for ( i in [0, 1, 2, 3] ) {
x += i
}
assert x == 6
// 遍历 Map 中的值
def map = ['a':1, 'b':2, 'c':3]
x = 0
for ( v in map.values() ) {
x += v
}
assert x == 6
1.2.3.2 switch语句
Groovy
中的Switch
语句不仅兼容Java
代码,还可以处理更多类型的case
表达式。case
表达式可以是字符串、列表、范围、Integer
等等
def x = 16
def result = ""
switch ( x ) {
case "ok":
result = "found ok"
case [1, 2, 4, 'list']:
result = "list"
break
case 10..19:
result = "range"
break
case Integer:
result = "integer"
break
default:
result = "default"
}
assert result == "range"
1.2.4 方法
⽅法使⽤返回类型或def
关键字定义,⽅法可以接收任意数量的参数,这些参数可以不申明类型, 如果不提供可⻅性修饰符,则该⽅法为public
。 ⽤def
关键字定义⽅法。
def add(int a,int b) {
println a+b // 3
}
def minus(a,b) {// 2
println a-b
}
add (1,2)
minus 1,2 // 1
如果指定了⽅法返回类型,可以不需要def
关键字来定义⽅法。
int minus(a,b) {
return a-b
}
def number=minus 1,2
println number
如果不使⽤return
,⽅法的返回值为最后⼀⾏代码的执⾏结果。
int minus(a,b) {
a-b //4
}
从上⾯两段代码中可以发现Groovy
中有很多省略的地⽅:
- 语句后⾯的分号可以省略。
- ⽅法的括号可以省略,⽐如注释1和注释3处。
- 参数类型可以省略,⽐如注释2处。
return
可以省略掉,⽐如注释4处。
1.2.5 闭包
Groovy
中的闭包是⼀个开放的、匿名的、可以接受参数和返回值的代码块。
闭包的定义遵循以下语法:
{ [closureParameters -> ] statements }
闭包分为两个部分,分别是参数列表部分[closureParameters -> ]
和语句部分statements
。 参数列表部分是可选的,如果闭包只有⼀个参数,参数名是可选的,Groovy
会隐式指定it
作为参数名,如下所示。
{ println it } // 使⽤隐式参数 it 的闭包
当需要指定参数列表时,需要 ->
将参数列表和闭包体相分离。
{ it -> println it } // it 是⼀个显示参数
{ String a, String b ->
println "${a} is a ${b}"
}
闭包是groovy.lang.Closure
类的⼀个实例,这使得闭包可以赋值给变量或字段,如下所示。
// 将闭包赋值给⼀个变量
def println ={ it -> println it }
assert println instanceof Closure
// 将闭包赋值给Closure类型变量
Closure do= { println 'do!' }
闭包既可以当做⽅法来调⽤,也可以显示调⽤call
⽅法。
def code = { 123 }
assert code() == 123 // 闭包当做⽅法调⽤
assert code.call() == 123 // 显示调⽤ call ⽅法
def isOddNumber = { int i -> i%2 != 0 }
assert isOddNumber(3) == true // 调⽤带参数的闭包
闭包作为⽅法的参数
// 闭包作为⽅法的参数
// ⽆参数的闭包
def test1(Closure closure){
println "test1 start......"
closure();//调⽤闭包
println "test1 end......"
}
test1 {println "hahahaha......"}
println("---------------------------");
//有参数的闭包
def test2(Closure closure){
int a = 100;
int b = 200;
println "test1 start......"
closure(a,b);//调⽤闭包
println "test1 end......"
}
test2 {x,y->{
println("x:"+x);
println("y:"+y);
}}
1.2.6 类
Groovy
类⾮常类似于Java
类。
def p = new Person()
p.increaseAge 5
println p.age
class Person {
String name
Integer age =10
def increaseAge(Integer years) {
this.age += years
}
}
Groovy
类与Java
类有以下的区别:
- 默认类的修饰符为
public
。 - 没有可⻅性修饰符的字段会⾃动⽣成对应的
setter
和getter
⽅法。 - 类不需要与它的源⽂件有相同的名称,但还是建议采⽤相同的名称。