Gradle构建过程概览

参考链接:

《Gradle权威指南》(京东购买)
《精通Groovy》
《Groovy官网闭包文档》
《Boyce》

HelloWorld

假设条件:

此时你已经有了JDK6.0以上版本,并配置了环境变量。
此时已经在你的电脑上安装了Android Studio并且新建了一个最简单的工程。

下载并解压:从Gradle官方网站上下载Gradle的发行包。

1
2
3
4
5
6
7
8
9
10
11
12
gradle-package
|
├─bin # Gradle可执行文件
├─doc
| ├─userguide # 用户指南(HTML和PDF两种版本)
| ├─javadoc # API文档(包括Javadoc和Groovydoc)
| ├─dsl # DSL参考指南。
|
├─lib # 库文件(jar包)
├─media # 一些资源文件(比如图标)
├─samples # 大量的样例,包括用户指南里的例子
├─src # 源代码。这些源代码仅供参考,要编译Gradle需要去下载源码发行包。

配置环境变量:

GRADLE_HOME –> ~\gradle\gradle-4.6
PATH –> %GRADLE_HOME%\bin

测试安装结果:可以通过运行gradle命令或者gradle -v来查看是否安装成功。

新建android-gradle目录,并在其中新建文件build.gradle(Gradle自带了Groovy库,因此不需要安装Groovy)

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

在android-gradle目录执行命令gradle -q hello

1
2
$ gradle -q hello
>>> Hello World!

这里定义了一个任务(task),任务中执行了打印输出操作,接下来我们看看什么是任务。

项目和任务

在Gradle中有两个比较重要的概念就是项目(project)和任务(task),任何构建都是由一个或多个项目组成,一个项目由多个任务组成。

一个项目代表着什么,取决于你想通过Gradle来做什么。比如,一个项目可能代表着一个JAR库,或者是一个Web应用程序。它也可能代表从其他项目所生成的JAR包组装起来的ZIP文件。一个项目不一定是代表一个要构建的东西,它也可能代表一个要完成的东西,比如把您的应用部署到预发布或生产环境。

一个任务表示构建执行的一些原子工作,比如编译一些类,创建一个JAR包,生成javadoc,或者是把一些档案发布到仓库中。

Gradle Wrapper

Wrapper是对Gradle的一层包装,方便团队统一Gradle的构建版本。
Wrapper在Windows下是一个批处理脚本,在Linux下是一个shell脚本。

当我们使用Wrapper启动Gradle的时候会自动检查Gradle相关配置, Wrapper的工作流如下:

Wrapper的工作流

更多Gradle Wrapper的操作请查看官方文档

1
2
3
4
5
6
$ gradle wrapper
:wrapper

BUILD SUCCESSFUL

Total time: 2.804 secs

生成目录和文件如下:

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
hello
│ build.gradle
│ gradlew
│ gradlew.bat

├─.gradle
│ ├─4.6
│ │ ├─fileChanges
│ │ │ last-build.bin
│ │ │
│ │ ├─fileHashes
│ │ │ fileHashes.bin
│ │ │ fileHashes.lock
│ │ │
│ │ └─taskHistory
│ │ taskHistory.bin
│ │ taskHistory.lock
│ │
│ └─buildOutputCleanup
│ buildOutputCleanup.lock
│ cache.properties
│ outputFiles.bin

└─gradle
└─wrapper
gradle-wrapper.jar
gradle-wrapper.properties

其中gradlew和gradlew.bat分别是linux和windows下的执行脚本,gradle-wrapper.jar具体逻辑实现的jar包,gradle-wrapper.properties是配置文件。

1
2
3
4
5
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
字段名说明
distributionBase下载的Gradle压缩包解压后存放的主目录
distributionPath解压后路径
distributionUrlGradle发行版压缩包的下载地址
zipStoreBase同distributionBase,只不过是存放zip压缩包的
zipStorePath同distributionPath,只不过是存放zip压缩包的

我们可以在build.gradle中自定义wrapper Task来修改生成的配置。

1
2
3
4
task wrapper(type: Wrapper){
gradleVersion = '2.4'
distributionBase= 'GRADLE_USER_HOME'
}

同样可以配置上面的其他参数。

Gradle的语法组成

Gradle不单单是一个配置脚本,它的背后是几门语言:

  • Groovy Language
  • Gradle DSL
  • Android DSL

DSL全称Domain Special Language 领域特定语言。Gradle是基于Groovy的DSL.
Groovy是一种JVM语言,和Java类似也是编译成clss文件然后在JVM上运行的,我们可以使用Groovy和Java混写。

Grovvy快速入门

Grovvy相对于Java语法的特点:

  • Groovy 的松散的 Java 语法允许省略分号和修改符。
  • 除非另行指定,Groovy 的所有内容都为 public。
  • Groovy 允许定义简单脚本,同时无需定义正规的 class 对象。
  • Groovy 在普通的常用 Java 对象上增加了一些独特的方法和快捷方式,使得它们更容易使用。
  • Groovy 语法还允许省略变量类型。

HelloWorld示例

1
println "HelloWorld!"

没错,就是它,就是这么简单。再来看一个例子:

1
2
3
4
5
def repeat(val){
for(i in 1..5){
println val
}
}

这样可以循环5次来输出变量val的值,在Groovy中允许省略变量类型(这个和kotlin很像)

关于其他的语法特点这里就不多说了,请参考相关文档,接下来我们来看看特别重要的闭包的概念。

闭包是Groovy中一个特别重要的特性,可以说它是DSL的基础。

闭包本质上是能够读取其他函数内部变量的函数,是将函数内部和函数外部连接起来的一座桥梁。

JavaScript中的闭包请参考(http://dp2px.com/2018/10/19/web-js-object2/)

在Groovy中,闭包的概念更加的宽泛,不像javascript那样必须是返回函数。在Groovy中,只要是一个代码块就可以叫做闭包,可以作为方法参数,可以作为返回值, 可以单独被调用。为什么Groovy中的闭包这么牛逼?来看Groovy官网的一段闭包的描述:

A closure is an instance of the groovy.lang.Closure class, making it assignable to a variable or a field as any other variable, despite being a block of code:

1
2
3
4
5
6
7
8
9
10
{ item++ }                                                                        

{ String x, int y ->
println "hey ${x} the value is ${y}"
}

def closure = { reader ->
def line = reader.readLine()
line.trim()
}

所以,在Groovy中,闭包就是类Closure,定义一个闭包就是定义一个Closure类。我们知道,在Java中 其实类就可以看成是闭包。因此我们也可以说Groovy中其实没有闭包,Groovy所谓的闭包只不过是提供一种 简便的语法支持来构建简单的类。

当然,Groovy中的这种闭包写起来还是很便利,但是我们在使用Groovy的时候,往往会面临一个问题, 这个地方是用一个方法还是用一个闭包?

1
2
3
4
5
6
7
def method(def arg1, def arg2) {
println("method: $arg1 + $arg2")
}

def closure = {def arg1, def arg2 ->
println("closure: $arg1 + $arg2")
}

为了回答这个问题其实也很简单,只要清楚Groovy中闭包的本质是一个类就可以,在Groovy或Java 中,类是唯一的“可调用对象”,唯一可以作为参数和返回值的对象。所以,使用Groovy的时候只有在 一种情况下建议使用闭包,就是在Java中这个地方你不得不使用类但是又觉得使用类有点多余的时候, 或者你在不得不使用匿名内部类的时候,就使用Groovy提供的便利构建类的方式——闭包。其他情况, 用方法。

所以在一个类中,把一个方法定义成闭包,但是这个闭包不作为方法参数,也不作为方法返回值时,我觉得 是没有任何意义的,除了语法上让别人觉得你懂点Groovy之外。

Gradle任务构建入门

settings.gradle文件

settings.gradle文件大多数的作用是配置子工程的。

1
include ':app'

build.gradle文件

每个Project都会有一个build.gradle文件,这个是构建入口,可以在这里配置版本、需要的插件、依赖库等。

在开发大型项目的时候我们一个Java工程,会被划分为很多小模块,每个模块对应一个child project,这个时候我们可以统一配置也可以分别配置。

project和Task

上面已经提到了,一个工程我们可以分为很多个project,每个project可以有一个单独的build.gradle配置,每个project是由多个Task组成的。

Task是一个原子性的操作,比如打个jar包、复制一份文件、编译一次Java代码等。

1
2
3
4
5
6
7
8
task customeTask1{
doFirst{
println 'customeTask1:doFirst'
}
doLast{
println 'customTask2:doLast'
}
}

Task任务之间可能存在依赖关系,不如我们允许jar之前需要执行编译(compile)任务。

1
2
3
4
5
6
7
8
9
task compile << {  //注意这里使用了快捷符号
println 'compile'
}

task runjar(dependsOn: compile){ //依赖关系
doLast {
println 'run jar'
}
}

Task可以通过API的方式来操作任务,注意脚本前后顺序。

1
2
3
4
5
6
7
8
9
10
11
task ex36Hello << {
println 'dowLast1'
}

ex36Hello.doFirst{
println 'dowFrst'
}

ex36Hello.doLast{
println 'dowLast'
}

另外,上面的”<<”操作符在Task上是doLast方法的短标记形式。

默认任务

Gradle允许你在构建中使用defaultTasks定义一个或多个的默认任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
defaultTasks 'clean', 'run'

task clean << {
println 'Default Cleaning!'
}

task run << {
println 'Default Running!'
}

task other << {
println "I'm not a default task!"
}

这样我们执行gradle -q不用指定任务,就会执行默认任务clean和run,在多项目构建中,每一个子项目都可以有它自己的指定的默认任务。如果一个子项目没有指定默认任务,而父项目定义了的话,那么将会使用父项目的。

DAG

Gradle有一个配置阶段和一个执行阶段,可以在配置阶段使用钩子方法来根据条件来检查和配置具体的值,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
task distribution << {
println "We build the zip with version=$version"
}

task release(dependsOn: 'distribution') << {
println 'We release now'
}

gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}

执行gradle -q distribution会发现输出为We build the zip with version=1.0-SNAPSHOT

gradle.taskGraph.whenReady会在任务执行之前,执行并影响上面定义的$version,来根据条件判断赋予特定的值。

Gradle插件

插件的应用可以通过Project.apply()方法来完成,apply方法有好几种用法,并且插件也分为二进制插件和脚本插件。

二进制插件

二进制插件是实现了org.gradle.api.Plugin接口的插件,它可以有plugin id

1
apply plugin:'java'

其中java是插件的plugin id它是唯一的。Gradle自带的核心插件都有一个plugin id,其实它对应的是org.gradle.api.plugins.JavaPlugin

等价于

1
2
3
apply plugin:org.gradle.api.plugins.JavaPlugin
或者
apply plugin:JavaPlugin

脚本插件

build.gradle文件

1
2
3
apply from:'version.gradle'

...

version.gradle文件

1
2
3
4
ext{
versionName = '1.0.0'
versionCode = 1
}

第三方插件

第三方插件作为jar的二进制插件,我们在应用的时候,必须要在buildscript{}中配置classpath才能使用。

1
2
3
4
5
6
7
8
buildscript{
repositories{
jcenter()
}
dependencies{
classpath 'com.android.tools.build:gradle:1.5.0'
}
}

buildscript{}块是一个在项目构建之前,为项目进行前期准备和初始化相关配置依赖的地方。

例如上面就是Android Gradle的jar依赖路径

1
apply plugin: 'com.android.application'

自定义插件

1
2
3
4
5
6
7
8
9
10
apply plugin: CustomePlugin

class CustomePlugin implements Plugin<Project>{

void apply(Project project){
project.task('CustomePlugin') << {
println "这是一个通过自定义插件方式创建的任务"
}
}
}

自定义插件必须实现Plugin接口,可以使用 gradlew : projname : CustomeTask来执行这个任务。

更多关于自定义插件的知识这里不涉及。

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage Element Error Exception Extensions File FileProvider Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code ValueAnimator ViewPropertyAnimator Web Web前端 Workbench api apk bookmark by关键字 compileOnly css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指针 插件 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 源码 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×