【Android开发高级系列】Gradle专题

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 参考链接

GoodGradle习总结——根本上看透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

Gradle系列之二——创建Task的多种方法

http://www.cnblogs.com/davenkin/p/gradle-learning-2.html

Gradle系列之三——读懂Gradle语法

http://www.cnblogs.com/davenkin/p/gradle-learning-3.html

Gradle系列之四——增量式构建

https://www.cnblogs.com/CloudTeng/p/3418260.html

Gradle系列之五——自定Property

http://www.cnblogs.com/davenkin/p/gradle-learning-5.html

Gradle系列之六——使用Java Plugin

Gradle系列之七——管理

Gradle系列之八——构建多个Project

Gradle系列之九——自定Task类型

Gradle系列之十——自定Plugin(本系列完)

理解与配置Android studio中的gradle

http://blog.csdn.net/u011913612/article/details/51732632

Android StudioGradle使用

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

GoodAndroid 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 中的自定配置信息

http://blog.csdn.net/winnershili/article/details/78532833

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容