要彻底理解Gradle的行为,需要了解一点Groovy语言。
一些基础
简单来说,Gradle是使用Groovy语言编写的一个框架,主要用于建构。作为框架,主要的基础设施都已经做好了。那么作为用户的我们只需要调用接口完成完成特定的任务即可。
首先,要明白你编写的所有的Gradle脚本都是配置脚本,也就是所有.gradle
后缀文件,都是为了配置几个基本对象(Project,Gradle,Settings)。要配置的对象是由Gradle创建的,我们编写的脚本就运行在这个对象的上下文中对它进行配置,因而也称这些对象是脚本文件的委托对象(delegate object)。Gradle
对象是一个全局的对象,一般使用默认配置即可,不需要对其编写脚本。
Gradle的构建过程:
- 创建一个Settings对象
- 首先是初始化阶段,项目中如果存在
settings.gradle
文件,则使用该文件配置Settings对象。 - 一般在
settings.gradle
文件中调用Settings
对象的include
方法,传入各个子项目,如include ':app', ':mylibrary'
。(也就是Android Studio里面的模块) - 然后就是配置阶段,该阶段解析每个project中的
build.gradle
。解析每个子目录中的build.gradle。在这两个阶段之间,我们可以加一些定制化的Hook。这当然是通过API来添加的。 - Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。恩?前面说过,一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个HOOK,即当Task关系图建立好后,执行一些操作。
- 最后一个阶段就是执行任务了。当然,任务执行完后,我们还可以加Hook。
Project对象
例如使用Android Studio新建项目的顶层目录中的build.gradle
文件,是用来配置Project
对象的,一个build.gradle文件对应一个Project
对象。因此在脚本文件里面我们就可以调用Project
对象提供的API,说白了就是它的属性和方法。那么有哪些属性和方法可以使用呢?当然是看Gradle提供的API文档了。
假如build.gradle
文件内容如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
这个脚本首先调用Project
的buildscript方法,并且传入一个closure参数,注意在该closure中调用的repositories和dependencies方法并非是Project对象的方法。这是因为buildscript方法在执行该closure之前将它委托(delegate)到了另一个对象上(ScriptHandler)。因而关于这两个方法的使用应该到ScriptHandler里面去查找。这两个方法的closure参数同样被分别委托到了RepositoryHandler和DependencyHandler对象上。
关于Groovy中closure的委托,参看官网的说明http://www.groovy-lang.org/closures.html#_delegation_strategy。
allprojects方法同理。
接下来重点说说,一个Project的基本组成部分,也是真正干活的东西,那就是Task。一个Task包含若干Action。所以,Task有doFirst和doLast两个函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包。”<<”操作符等同于doLast。
上面脚本中,调用Project对象的task方法创建一个名为clean的Task。嗯?task是方法名,那clean是啥玩意。这明显不符合Groovy的语法。其实创建task的时候确实是一个标准的Groovy的方法调用,等同于
task('clean', type: Delete) {
}
显然Gradle在这里的语法上做了一点手脚,转换了一下。所以这也说明,学习Gradle不一定非要在Groovy的基础上刨根问底。毕竟Gradle定义了自己的DSL。我们最终使用的是Gradle,直接按Gradle定义的一套来就行了。