本文将对Gradle的一些基本概念、用法进行总结,从而避免自己在Android开发过程中的“会而不知所以然”,关于Gradle的高阶用法可以见参考,本文就不再赘述;
基本概念
Wrapper
为了保证gradle版本的一致性,比如我们下载某个Android工程,编译运行前,wrapper会检查你本地是否存在gradle-wrapper.properties文件中声明的gradle版本,如果不存在,就会去下载,通常是个gradlew配合一起使用的,如下示例,可以帮助你更好的理解wrapper的概念;
[min@TATEMIN-MB0:] gradle $ mkdir wrapper-demo # 创建一个空的wrapper-demo
[min@TATEMIN-MB0:] gradle $ cd wrapper-demo/
[min@TATEMIN-MB0:] wrapper-demo $ gradle -v # 查看当前gradle的版本
------------------------------------------------------------
Gradle 5.4.1
------------------------------------------------------------
Build time: 2019-04-26 08:14:42 UTC
Revision: 261d171646b36a6a28d5a19a69676cd098a4c19d
Kotlin: 1.3.21
Groovy: 2.5.4
Ant: Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM: 1.8.0_241 (Oracle Corporation 25.241-b07)
OS: Mac OS X 10.15.4 x86_64
[min@TATEMIN-MB0:] wrapper-demo $ gradle wrapper # 创建一个gradle wrapper
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
[min@TATEMIN-MB0:] wrapper-demo $ tree
.
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
2 directories, 4 files
# 查看wrapper的内容,可以看到distributionUrl的版本号和我们本地的对齐,此工程如果那运作在一台没有安装过gradle-5.4.1的设备上,会先去下载gradle-5.4.1对应的包,此过程由gradlew+gradle-wrapper.jar完成;
[min@TATEMIN-MB0:] wrapper-demo $ cat gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
GradleUserHome
在Mac/Linux系统上,此目录即为~/.gradle目录,可以理解为gradle的workspace。
Daemon
因为JVM启动速度很慢,大约每次启动在10s左右,所以gradle为了解决这个问题,将gradle设计为如下结构:
大体的意思即使说,在启动gradle后会同时启动两个进程,一个Client,一个Daemon,Client负责具体的交互,比如命令的接收、日志的打印等等,Daemon负责具体的编译工作并和Client完成交互,Client端推出后,Daemon并不立即退出,如果期间没有新的Client发消息进来,会存活3小时左右,这样就避免了每次gradle启动时等待JVM启动的耗时,另外如果当前启动的Client版本和Daemon不一致,会重新启动一个Deamon。
Daemon 4.0以前,CI相关编译建议加上--no-daemon,因为此时Daemon并不稳定,后续版本无此问题。
Groovy基础
动态调用和MOP
MOP:元对象协议。由 Groovy 语言中的一种协议。该协议的出现为元编程提供了优雅的解决方案。而 MOP 机制的核心就是 MetaClass。
元编程:编写能够操作程序的程序,也包括操作程序自身,示例如下:
string = 'hello'
targetMethod = string.metaClass.getMetaMethod('toUpperCase')
println(targetMethod.invoke(string))
闭包
-
闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量;
-
闭包定义:{ [closureParameters -> ] statements },示例:
//执行一句话 { printf 'Hello World' } //闭包有默认参数it,且不用申明 { println it } //闭包有默认参数it,申明了也无所谓 { it -> println it } // name是自定义的参数名 { name -> println name } //多个参数的闭包 { String x, int y -> println "hey ${x} the value is ${y}" }
-
Android示例:
def android(closure) = { closure() } def ext(closure) = { closure() } android { ext { println("i am ext") } }
Gradle 构建
主要理解gradle的核心模型
-
Project
每个Gradle构建都由一个或多个Project组成。
-
Task
每个Project由一个或多个Task组成,Task代表构建执行的一些原子工作,比如编译某些类、创建JAR、生成Javadoc等等;
-
Lifecycle与Hook
Gradle构建的声明周期都分为三个不同的阶段.
-
Initialization
Gradle支持单项目和多项目构建. 在初始化阶段,Gradle确定将要参与构建的Project,并为每个Project创建一个Project实例(构建文件与Gradle交互的主要API.)
-
Configuration
在此阶段,将配置项目对象,执行作为构建一部分的所有项目的构建脚本.
-
Execution
Gradle确定要在配置阶段创建和配置的任务子集. 子集由传递给
gradle
命令的任务名称参数和当前目录确定. 然后Gradle执行每个选定的任务.
-
下面是一个非常好的例子可以理解一下:
task("hello_world") {
println("configuration")
doLast {
println("execution task")
}
}
(1..10).each { i ->
task("task_${i}") {
int index = i
if (index % 2 == 0) {
dependsOn("hello_world")
}
doLast {
println("executing task_${index}")
}
}
}
afterEvaluate {
println("after evaluate")
}
此脚本最后执行结果如下:
[min@TATEMIN-MB0:] wrapper-demo $ ./gradlew task_2
> Configure project : # Configuration阶段
configuration
after evaluate
> Task :hello_world # Execution阶段
execution task
> Task :task_2 # Execution阶段
executing task_2
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
网友评论