Gradle学习系列之三——读懂Gradle语法
在本系列的上篇文章中,我们讲到了创建Task的多种方法,在本篇文章中,我们将学习如何读懂Gradle。
请通过以下方式下载本系列文章的Github示例代码:
git clone https://github.com/davenkin/gradle-learning.git
Gradle是一种声明式的构建工具。在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,然后才是实际的执行阶段。在配置阶段,Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。
虽然很多时候我们只需要照着网上的例子写自己的DSL语句就行了,但是此时我们所知道的也就只有这么多了。如果我们能够了解Gradle DSL的内部工作机制,那么我们便可以达到举一反三的效果。在前面的文章中我们讲到,Gradle的DSL只是Groovy语言的内部DSL,也即必须遵循Groovy的语法规则。现在,让我们先来看看以下非常简单的Task:
task showDescription1 << {
description = 'this is task showDescription'
println description
} task showDescription2 << {
println description
}
showDescription2.description = 'this is task showDescription' task showDescription3 << {
println description
} showDescription3 {
description = 'this is task showDescription'
}
以上3个Task完成的功能均相同,即先设置Task的description属性,在将其输出到命令行。但是,他们对description的设置方式是不同的。对于showDescription1,我们在定义一个Task的同时便设置description;对于showDescription2,其本身便是Project的一个Property;而对于showDescription3,我们是在一个和它同名的方法中设置description。
事实上,对于每一个Task,Gradle都会在Project中创建一个同名的Property,所以我们可以将该Task当作Property来访问,showDescription2便是这种情况。另外,Gradle还会创建一个同名的方法,该方法接受一个闭包,我们可以使用该方法来配置Task,showDescription3便是这种情况。
要读懂Gradle,我们首先需要了解Groovy语言中的两个概念,一个Groovy中的Bean概念,一个是Groovy闭包的delegate机制。
Groovy中的Bean和Java中的Bean有一个很大的不同,即Groovy为每一个字段都会自动生成getter和setter,并且我们可以通过像访问字段本身一样调用getter和setter,比如:
class GroovyBeanExample {
private String name
} def bean = new GroovyBeanExample()
bean.name = 'this is name'
println bean.name
我们看到,GroovyBeanExample只定义了一个私有的name属性,并没有getter和setter。但是在使用时,我们可以直接对name进行访问,无论时读还是写。事实上,我们并不是在直接访问name属性,当我们执行"bean.name = 'this is name'"时,我们实际调用的是"bean.setName('this is name')",而在调用"println bean.name"时,我们实际调用的是"println bean.getName()"。这里的原因在于,Groovy动态地为name创建了getter和setter,采用像直接访问的方式的目的是为了增加代码的可读性,使它更加自然,而在内部,Groovy依然是在调用setter和getter方法。这样,我们便可以理解上面对showDescription2的description设置原理。
另外,Gradle大量地使用了Groovy闭包的delegate机制。简单来说,delegate机制可以使我们将一个闭包中的执行代码的作用对象设置成任意其他对象。比如:
class Child {
private String name
} class Parent {
Child child = new Child(); void configChild(Closure c) {
c.delegate = child
c.setResolveStrategy Closure.DELEGATE_FIRST
c()
}
} def parent = new Parent()
parent.configChild {
name = "child name"
} println parent.child.name
在上面的例子中,当我们调用configChild()方法时,我们并没有指出name属性是属于Child的,但是它的确是在设置Child的name属性。事实上光从该方法的调用中,我们根本不知道name是属于哪个对象的,你可能会认为它是属于Parent的。真实情况是,在默认情况下,name的确被认为是属于Parent的,但是我们在configChild()方法的定义中做了手脚,使其不再访问Parent中的name(Parent也没有name属性),而是Child的name。在configChild()方法中,我们将该方法接受的闭包的delegate设置成了child,然后将该闭包的ResolveStrategy设置成了DELEGATE_FIRST。这样,在调用configChild()时,所跟闭包中代码被代理到了child上,即这些代码实际上是在child上执行的。此外,闭包的ResolveStrategy在默认情况下是OWNER_FIRST,即它会先查找闭包的owner(这里即parent),如果owner存在,则在owner上执行闭包中的代码。这里我们将其设置成了DELEGATE_FIRST,即该闭包会首先查找delegate(本例中即child),如果找到,该闭包便会在delegate上执行。对于上面的showDescription3,便是这种情况。当然,实际情况会稍微复杂一点,比如showDescription3()方法会在内部调用showDescription3的configure()方法,再在configure()方法中执行闭包中的代码。
你可能会发现,在使用Gradle时,我们并没有像上面的parent.configChild()一样指明方法调用的对象,而是在build.gradle文件中直接调用task(),apply()和configuration()方法等,这是因为在没有说明调用对象的情况下,Gradle会自动将调用对象设置成当前Project。比如调用apply()方法和调用project.apply()方法的效果是一样的。查查Gradle的Project文档,你会发现这些方法都是Project类的方法。
另外举个例子,对于configurations()方法(它的作用我们将在后面的文章中讲到),该方法实际上会将所跟闭包的delegate设置成ConfigurationContainer,然后在该ConfigurationContainer上执行闭包中的代码。再比如,dependencies()方法,该方法会将所跟闭包的delegate设置成DependencyHandler。
还有,Project还定义了configure(Object object,Closure configureClosure)方法,该方法是专门用来配置对象的(比如Task),它会将configureClosure的delegate设置成object,之后configureClosure中的执行代码其实是在object上执行的。和Groovy Bean一样,delegate机制的一个好处是可以增加所创建DSL的可读性。
在下一篇文章中,我们将讲到如何进行增量式构建。
Gradle学习系列之三——读懂Gradle语法的更多相关文章
- Gradle学习系列之读懂Gradle语法
转载地址: http://www.cnblogs.com/CloudTeng/p/3418072.html Gradle是一种声明式的构建工具.在执行时,Gradle并不会一开始便顺序执行build. ...
- Gradle学习系列之一——Gradle快速入门
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...
- Gradle学习系列之一——Gradle快速入门(转)
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...
- Gradle学习系列之四——增量式构建
在本系列的上篇文章中,我们讲到了如何读懂Gradle的语法,在本篇文章中,我们将讲到增量式地构建项目. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...
- Gradle学习系列之二——创建Task的多种方法
在本系列的上篇文章中,我们讲到了Gradle入门,在本篇文章中我们将讲到创建Task的多种方法. 请通过以下方式下载本系列文章的Github示例代码: git clone https://github ...
- CAN总线学习系列之三——CAN控制器的选择
CAN总线学习系列之三——CAN控制器的选择 在进行CAN总线开发前,首先要选择好CAN总线控制器.下面就比较一些控制器的特点. 一些主要的CAN总线器件产品 制造商 产品型号 器件功能及特点 Int ...
- SQL Sever 学习系列之三
SQL Sever 学习系列之三 SQL Server 学习系列之一(薪酬方案+基础) SQL Server 学习系列之二(日期格式问题) 五.经理今天刚谈到with的用法(with的 ...
- Gradle学习系列(一)
今天就开始学习Gradle构建了,听说很牛X.本篇内容就带领我初步窥探Gradle的世界. 1.什么是Gradle 相信之前都接触过用Ant或者Meavn进行项目的构建,两者各有千 ...
- 读懂 Gradle 的 DSL
现在 Android 开发免不了要和 Gradle 打交道,所有的 Android 开发肯定都知道这么在 build.gradle 中添加依赖,或者添加配置批量打包,但是真正理解这些脚本的人恐怕很少. ...
随机推荐
- Spark随机森林实现学习
前言 最近阅读了spark mllib(版本:spark 1.3)中Random Forest的实现,发现在分布式的数据结构上实现迭代算法时,有些地方与单机环境不一样.单机上一些直观的操作(递归),在 ...
- 超体.特效中英字幕.Lucy.2014.BD1080P.X264.AAC.English&Mandarin.CHS-ENG
资源名称 其它信息 资源大小 BT下载 超体.Lucy.2014.BD-MP4-原创翻译中英双语字幕.mp4 seeders: / leechers: 511.15MB 下载 [飘域家园]移动迷宫.T ...
- Python中import的使用
python中的import语句是用来导入模块的,在python模块库中有着大量的模块可供使用,要想使用这些文件需要用import语句把指定模块导入到当前程序中. import语句的作用 import ...
- 记录一下,如何配置nodejs nginx的反向代理
本文是在mac下配置nodejs 在nginx下的反向代理 1.安装nodejs,之前就安装了. 2.安装nginx ,我采用的直接源码安装 3.进入 /usr/local/nginx/conf 目录 ...
- android开发常见编程错误总结
1.设置TextView的文本颜色 1 2 3 TextView tv; ... tv.setTextColor(R.color.white); 其实这样设置的颜色是 R.color.white的资源 ...
- js/jquery 实时监听输入框值变化的完美方案:oninput & onpropertychange
(1) 先说jquery, 使用 jQuery 库的话,只需要同时绑定 oninput 和 onpropertychange 两个事件就可以了,示例代码: $('#username').bin ...
- 使用 Windows10 自定义交互消息通知
消息通知是最常用的应用功能之一了,但是由于平台的差异,IOS Android 以及 Windows 都有其特殊性,Android开发者在国内常常都是使用三方的一些推送服务,或者是使用自建的服务器为应用 ...
- <转载> 优秀程序员必备的23条好习惯
转自 优秀程序员必备的23条好习惯 编程是一项聪明人玩的游戏,它既是对智力的考验,也是对习惯的考验,智力的好坏取决于父母的基因,人们无从左右,但习惯的好坏却是可以不断培养.一项由美国芝加哥大学国家研究 ...
- 再谈LRU双链表内存管理
N年前我写了个双链表也发了博客,还添了代码.但是那个代码不但复杂,而且还有有问题的,一直懒得整理,放在空间误导别人.最近在写服务端,今天抽点空补一篇. 关于LRU网上随便搜,有过后端经验的人应该很多都 ...
- IOS实现中间凸起圆形TabBar
中间凸起的圆形TabBar曾经比较流行,类似于闲鱼之类的APP就使用了中间凸起TabBar,这两天自己动手实现了一个,效果图如下: 大致原理:重写UITabBar和UITabBarController ...