1 关于Groovy
Gradle是一种声明式的构建工具。在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,然后才是实际的执行阶段。
配置阶段,Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。
Gradle的DSL只是Groovy语言的内部DSL,也必须遵循Groovy的语法规则。Groovy语言中的两个概念,一个是Groovy中的Bean概念,一个是Groovy闭包的delegate机制。
1.1 bean
Groovy中的Bean和Java中的Bean有一个很大的不同,即Groovy动态的为每一个字段都会自动生成getter和setter,并且我们可以通过像访问字段本身一样调用getter和setter
class GroovyBeanExample {
private String name
}
def bean = new GroovyBeanExample()
bean.name = 'this is name' //实际调用的是"bean.setName('this isname')"
println bean.name //实际调用的是"println bean.getName()"
采用像直接访问的方式的目的是为了增加代码的可读性,使它更加自然,而在内部,Groovy依然是在调用setter和getter方法。
2 闭包的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上执行。
2.1 联想gradle中声明的方法
在使用Gradle时,我们并没有像上面的parent.configChild()一样指明方法调用的对象,而是在build.gradle文件中直接调用task(),apply()和configuration()等方法。这是因为在没有说明调用对象的情况下,Gradle会自动将调用对象设置成当前Project。比如调用apply()方法和调用project.apply()方法的效果是一样的。查查Gradle的Project文档,你会发现这些方法都是Project类的方法。
对于configurations()方法,该方法实际上会将所跟闭包的delegate设置成ConfigurationContainer,然后在该ConfigurationContainer上执行闭包中的代码。再比如,dependencies()方法,该方法会将所跟闭包的delegate设置成DependencyHandler。
3 Gradle语法
3.1 Gradle简介
Gradle本身的领域对象主要有Project和Task。
Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。
一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。是不是很屌的样子。
task关键字其实是一个groovy中方法的调用,该方法属于Project,而大括号之间的内容则表示传递给task()方法的一个闭包。
3.2 task间的依赖
task之间是可以存在依赖关系,比如TaskA依赖TaskB,那么在执行TaskA时,Gradle会先执行TaskB,再执行TaskA。我们可以在定义一个Task的同时声明它的依赖关系:
task helloWorldFour(dependsOn:helloWorldThree) << {
println 'hellohelloWorldFour'
}
或者
task helloWorldFour << {
println 'hellohelloWorldFour'
}
helloWorldFour.dependsOnhelloWorldThree
3.3 可配置的task
一个Task除了执行操作之外,还可以包含多个Property,其中有Gradle为每个Task默认定义了一些Property,比如description,logger等。另外,每一个特定的Task类型还可以含有特定的Property,比如Copy的from和to等。当然,我们还可以动态地向Task中加入额外的Property。在执行一个Task之前,我们通常都需要先设定Property的值。
task helloWorld << {
description = "this ishelloWorld"
println description
}
或者通过调用Task的configure()方法完成Property的设置:
task helloWorld << {
println description
}
helloWorld.configure{
description = "this ishelloWorld"
}
3.4 花式task
task showDescription1 << {
description = 'this is taskshowDescription'
println description
}
task showDescription2 << {
println description
}
showDescription2.description = 'this is task showDescription'
task showDescription3 << {
println description
}
showDescription3{
description = 'this is taskshowDescription'
}
对于每一个Task,Gradle都会在Project中创建一个同名的Property,所以我们可以将该Task当作Property来访问,showDescription2便是这种情况。另外,Gradle还会创建一个同名的方法,该方法接受一个闭包,我们可以使用该方法来配置Task,showDescription3便是这种情况。
3.5 自定义Property
Gradle还为我们提供了多种方法来自定义Project的Property。
3.5.1 在build.gradle文件中定义Property
添加一个名为property1的Property:
ext.property1 = "this is property1"
或者采用闭包的形式
ext{
property2 = "this isproperty2"
}
定义了Property后,使用这些Property时我们则不需要ext,而是可以直接访问:
task showProperties << {
println property1
println property2
}
还可以在执行命令行的时候加属性
task showCommandLieProperties << {
println property3
}
//以下是cmd中执行命令
gradle-Property3 = "this is property3" showCommandLieProperties
//通过JVM系统参数定义Property,与java类似,但是前面要约定以“org.gradle.project”为前缀
gradle-D org.gradle.project.property3 = "this is another property3" showCommandLieProperties
此外还可以通过环境变量来为Gradle设置Property,但是每一个Property都需要以 “ORG_GRADLE_PROJECT_”为前缀:
ORG_GRADLE_PROJECT_property3 = "thisis yet another property3"
3.6 Gradle的Plugin
Gradle最常用的Plugin便是java Plugin了。和其他Plugin一样,java Plugin并没有什么特别的地方,只是向Project中引入了多个Task和Property。当然,java Plugin也有比较与众不同的地方,其中之一便是它在项目中引入了构建生命周期的概念,就像Maven一样。但是,和Maven不同的是,Gradle的项目构建生命周期并不是Gradle的内建机制,而是由Plugin自己引入的。
3.7 依赖管理
一个项目总会依赖于第三方,要么是一个第三方类库,要么是自己开发的另一个module
配置Gradle的Repository,就是告诉Gradle在什么地方去获取这些依赖
repositories{
mavenCentral()
jCentral()
}
jCentral()是大于mavenCentral()的一个仓库,现在是studio默认的仓库
Gradle对依赖进行分组,允许编译时使用一组依赖,运行时使用另一组依赖。每一组依赖称为一个Configuration,在声明依赖时,我们实际上是在设置不同的Configuration。
要定义一个Configuration,我们可以通过以下方式完成:studio一般不需要设置,应该是有默认的,即为classpath。
configurations{
myDependency
}
通过dependencies()方法向myDependency中加入实际的依赖项:
dependencies{
//下面的myDependency是关键
myDependency 'org.apache.commons:commons-lang3:3.0'
}
//类似studio中的classpath
dependencies{
classpath 'com.android.tools.build:gradle:1.3.0'
}
//还有 这里的compile,testCompile
dependencies{
compile project(':library')
compile 'com.android.support:recyclerview-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
compile 'com.evernote:android-intent:1.0.1'
testCompile 'junit:junit:4.8.2'
}
myDependency,classpath,compile,testCompile都是Configuration(一组依赖)。除了myDependency都不是我们定义的, android Plugin会自动定义compile和testCompile分别用于编译Java源文件和编译Java测试源文件。classpath应该是用于所有,我类推的。
Gradle还允许我们声明对其他Project或者文件系统的依赖。
dependencies{
//library是另一个module的名字
compile project(':library')
}
对于本地文件系统中的Jar文件,我们可以通过以下方式声明对其的依赖:
dependencies{
//java compile files('spring-core.jar', 'spring-aap.jar')
compile fileTree(dir: 'deps', include:'*.jar')
//studio中一般这么写
compile fileTree(dir: 'libs', include:['*.jar'])
}
3.8 构建多module的project
Gradle为每个build.gradle都会创建一个相应的module领域对象,在编写Gradle脚本时,我们实际上是在操作诸如module这样的Gradle领域对象。在多module的项目中,我们会操作多个module领域对象。Gradle提供了强大的多module构建支持。
要创建多module的Gradle项目,我们首先需要在根(Root)Project中加入名为settings.gradle的配置文件,该文件应该包含各个子module(其实就是一个子project)的名称。如setting.gradle中:
include'library', 'demo'
类似module(子project)的build.gradle,(Root)Project也有自己的build.gradle,在里面通常设置:
allprojects{
repositories {
jcenter()
}
//通常studio项目没有,咱自己加的
apply plugin: 'idea'
task allTask << {
println project.name
}
}
allprojects()方法将repositories配置一次性地应用于所有的module(子Project)和root-project本身,当然也包括定义的Task,这个task配置到所有module里面了和root-project。
subprojects()方法用于配置所有的子Project(不包含根Project)。
4 参考链接
(Good)Gradle学习总结——根本上看透Android Studio构建
http://www.jianshu.com/p/60e556a968de
深入理解Android之Gradle
http://blog.csdn.net/innost/article/details/48228651
Gradle学习系列之一——Gradle快速入门
http://www.cnblogs.com/davenkin/p/gradle-learning-1.html
http://www.cnblogs.com/davenkin/p/gradle-learning-2.html
http://www.cnblogs.com/davenkin/p/gradle-learning-3.html
https://www.cnblogs.com/CloudTeng/p/3418260.html
http://www.cnblogs.com/davenkin/p/gradle-learning-5.html
理解与配置Android studio中的gradle
http://blog.csdn.net/u011913612/article/details/51732632
Android Studio中Gradle使用详解
https://www.jianshu.com/p/02cb9a0eb2a0
Android Studio Gradle命令和配置
https://www.2cto.com/kf/201704/626582.html
gradle深入理解以及在android studio中的使用
https://www.jianshu.com/p/1680700a974f
Android Gradle 语法简介
http://blog.csdn.net/wangbaochu/article/details/51177672
(Good)Android Gradle 完整指南
https://www.cnblogs.com/laughingQing/p/5855774.html
Cannot get property 'compileSdkVersion' on
extra properties extension as it does not exist问题解决
http://blog.csdn.net/u013768203/article/details/53140449
使用 代码 读取build.gradle 中的自定义配置信息