Gradle深入探索

 

Gradle如今已经成为Android工程不可或缺的构建工具,通过Gradle我们可以方便的进行依赖库,版本号,签名等各种项目配置。但是Gradle的功能却不止于此。

Gradle是一个基于Apache AntApache Maven概念的项目自动化建构工具。它使用一种基于Groovy特定领域语言来声明项目设置,而不是传统的XML

那么想要了解Gradle,我们就有必要好好了解一下Groovy这门语言。

GroovyJava平台上设计的面向对象编程语言。这门动态语言拥有类似PythonRubySmalltalk中的一些特性,可以作为Java平台的脚本语言使用,Groovy代码动态地编译成运行于Java虚拟机(JVM)上的Java字节码,并与其他Java代码和库进行互操作。由于其运行在JVM上的特性,Groovy可以使用其他Java语言编写的库。Groovy的语法与Java非常相似,大多数Java代码也匹配Groovy的语法规则,尽管可能语义不同。

可以看到groovy和java渊源颇深,它是基于jvm的语言,完全可以和java无缝衔接。对于我们基于java的android开发来说也相对友好。想要更加深入了解Gradle,我们有必要对Groovy进行一些了解。

Groovy基础语法

Groovy的语法很多和java相似。或是在java基础上进行扩展,使其更加方便易用

关键字

例如这是它的关键字:

as、assert、break、case、catch、class、const、continue、def、default、do、else、enum、
extends、false、finally、for、goto、if、implements、import、in、instanceof、interface、
new、null、package、return、super、switch、this、throw、throws、trait、true、try、while

可以看到几乎和java里的一样。

变量声明

在变量声明上有一些区别,因为Gradle是一门可选类型的动态变成语言,同时又具备了静态类型和静态编译的能力。具体来说:Groovy支持使用 def声明,不指定类型;也可以兼容Java风格,指定变量类型;甚至还可以省略def或类型

例如,

def a = 1
int b = 1
c     = 1

这些声明方式都是可以的。值得注意的是采用Groovy方式声明的变量不会预设类型,支持动态改变类型,只有在编译成class文件后类型才会确定。例如:

def a = '你好'
println a //打印你好
a = 5
println a + 1 //打印6 也不会报错

字符串GString

Groovy对字符串在java的String基础上进行了扩展。方便支持例如变量注入以及换行等操作

//单引号字符串,不支持占位符插值
a = 'abc'
//双引号字符串,支持占位符插值,如果没有则是java.lang.String类型
a = "world"
b = "hello $a"
x = 1
y = 2
println "x + y equals ${x + y}"
//三重引号字符串,支持直接换行,当然也分为三重双引号和三重单引号并保有其特性
a = ''' 
人生自古谁无死
或轻于鸿毛
或重于泰山
'''
//斜杠字符串 反斜杠不需要转义,通常用于正则表达式,支持多行,站位等
a = /abc/
b = /a
b
c/

方法

Groovy的方法就能体现出脚本语言的特性了,像java中的main方法也不是必须的

除此之外,调用方法可以不写括号,方法后边可以直接写参数。值得注意的是方法一定都有返回值。当方法没有return语句时,则返回最后一句的执行结果。当方法声明void时,返回null

println("Hello world!")
println "Hello world!" //两个结果一样,都不会报错

def fun(){
 retrun 1
}//返回1

def fun(){
   1
}//返回1

def void fun(){
    println"Hello"
}//返回null

闭包Closure

闭包是Groovy中比较重要的一个概念, 也正是利用了闭包,才有了Gradle现在的样子。我们可以把它理解成一个特殊的类,只不过不需要单独声明,只需要用大括号框起来即可。

闭包可以访问外部的变量和函数。

闭包可以有多个参数,并且至少会有一个it参数。

闭包的返回值和方法返回值规则相同。

调用闭包可以使用call方法,也可以省去.call直接使用小括号。

下面举几个例子:

def print = {
    println "print"
}

//调用
print()
print.call()

def sum = { a, b ->
  return a + b
}

//调用
sum.call(1, 2)
sum(1, 2)

以上是闭包的声明和调用,闭包还可以作为方法的参数传入。

//声明一个方法
def void greeting(String name, Closure closure){
    return closure(name)
}

//声明一个闭包

def doGreeting = { name ->
    println("Hello ${name} !")
}

//调用
greeting("Tom", doGreeting)


//还可以不提前声明 类似匿名内部类
greeting("Tom", { name ->
   println("Hello ${name} !")
})

//如果最后一个参数是闭包的话,还可以写在外边
greeting("Tom") { name ->
   println("Hello ${name} !")
}

我们再来看如果只有一个参数切是闭包的情况,就可以写成

//声明方法
def void greeting(Closure closure){
    return closure.call()
}

//声明闭包
def doGreeting = { 
    println("Hello !")
}

//调用
greeting(doGreeting)


//简化
greeting{
    println("Hello!")
}

可以看到只有一个参数的简化模式下,写法就和我们Gradle里的几乎一样了。 

当然这样简化虽然看起来很简单,但是实际使用中很容易写错。因为当你直接简化传入一个闭包时,看起来我们忽略了参数这件事,一个方法需要传入的参数个数,和这个闭包传入的参数个数必须一致,并且类型一致,才不会出问题。所以要想熟练运用这种写法还需要对Groovy的API以及代码上下文有一个熟练的把握。

除此之外,类似java类中的this,闭包也中也包含指代上下文的变量。不同的是闭包有三个:this, owner,delegate。先说一下定义,this在闭包中的含义指的是,表示定义该闭包的类的实例对象(实例闭包)或者类本身(静态闭包);owner通常和this一样,只有该闭包是在其他的闭包中定义的,那么owner是指向定义它的闭包对象;delegate通常和owner一样,但是它可以通过Closure.setDelegate()进行修改。

这三个概念解释起来有些抽象。让我们举个例子。我们上边介绍过了如何定义闭包。实际上定义闭包一定是在一个类里边的。那么闭包中的this,指代的就是定义闭包的这个类。例如,这样:

class Person{  
    def classClosure = { 
        //do something 
    }  
}  

这时候this指代的就是这个person这个类。并且在这种情况下owner,delegate和this一样。

def closure = {
    def innerClosure = {
        //do somethong
    }
}

closure.setDeletgate("abc")

这里我没有定义类,由于Groovy是支持脚本语言,所以这样直接写其实编译的时候还是会被定义成为一个脚本类。那么外层的closure的this和owner就是指代脚本类,delegate由于被修改了,就指代abc这个字符串。而内部的innerClosure,this同样是指代脚本类。它的owner和delegate则指代的是定义它的closure。

为什么要有三个这样的变量?这样就赋予了闭包非常强大的能力,要知道当我们去调用一个闭包的属性或方法的时候,如果闭包自身没有,那么就会去this,owner和delegate中找。这样就能为闭包扩展现有的属性和方法。至于具体查找顺序,也是可以自定义的。可以通过setResolveStrategy()方法定义先从哪里找,或者只从哪里找。

Groovy作为一门语言,还是比较复杂的,值得深入的研究,有兴趣的可以通过官网学习(http://docs.groovy-lang.org/docs/)。但是幸运的是对于我们理解并开发Gradle来说,用到的都是些相对简单的概念和特性。

Gradle DSL(Domain Specific Language)

Gradle的DSL无疑是我们每个开发者最熟悉的部分。或者说只要通过Android Studio构建一个工程,就不太可能绕过它。其实DSL就是谷哥帮我们定义的一套配置规范,我们只需要在框架内填写上我们自定义的参数就可以了。把原本复杂的程序代码简化为像是脚本一样简单。

这样我们即使对Groovy毫无了解,也能知道该在dependencies中添加依赖,在sourceSets中配置源码目录,在buildTypes中配置不同打包选项,当然还有compileSDKVersion,versionCode这些。更高级一点,可以在ext中配置一些全局变量,在subprojects中对配个module的配置进行同意操作,在uploadArchives中配置文件上传选项。这样简单的完成一个工程的配置。

而这些固定的字段,实际上是在接口中定义好的方法。其实通过上面对于Groovy的介绍我们也大致能了解了。每个方法的参数都是一个闭包。

具体到打包的时候,会分为三个步骤去执行

  1. 首先是Initiliazation阶段:对于我们的工程而言,就是执行 settings.gradle。找到settings.gradle中声明的每一个module并构建对应的project
  2. 然后是 Configration 阶段:Configration 阶段的目标是解析每个 project 中的 build.gradle。Configuration 阶段完了后,整个 build 的 project 以及内部的 Task 关系就确定了。一个Project 包含很多 Task,每个 Task 之间有依赖关系。Configuration 会建立一个有向图来描述 Task 之间的依赖关系。
  3. 最后一个阶段就是按照前面定义好的顺序逐条执行任务。

也就是说module中的每个build.gradle最后都会生成一个Project类,当然最外层的build.gradle同样也会生成一个project,也就是根project。里边定义里许多的我们熟悉的方法,以及引入插件例如com.android.library,com.android.application中的方法,就是一个一个的Task。

这里Gradle在设计的时候就充分用上了Groovy中闭包的特性,像我们熟悉的android,buildTypes,databinding,dependencies这些常用的配置项,我们已经知道他们实际上是一个个以闭包为参数的方法。但是在对应的插件中并没有实际的实现逻辑。而是把真正的实现类作为代理,替换我们之前介绍过的delegate,来实际执行。

 

我们可以在这里(https://docs.gradle.org/current/javadoc/index-all.html)以及这里(http://google.github.io/android-gradle-dsl/current/index.html)查看每一个我们想了解的接口。

例如我们在最熟悉的dependencies,它就是一个在Project中定义好的方法。注意看他的详细介绍,实际的实现并不在这个方法中,而是使用了我们之前说过的替换代理的方式。真正的实现是在DependencyHandler中。

实际的源码中也确实是这样的:

我们看到传入的 getDependencies()参数返回的实际上就是一个DependencyHandler类。

而configure方法最终把DependencyHandler作为configureClosure的delegate传入,这样在真正加载依赖的时候,会直接执行DependencyHandler中的方法去处理。

http://www.paincker.com/gradle-develop-basics

https://www.infoq.cn/article/android-in-depth-gradle

Gradle 自定义Task

介绍了这些,我们对Groovy和Gradle有了一个基本的了解。了解了Gradle的原理,只是为了帮助我们更好的进行基于Gradle的开发。接下来就来看一下如何基于Gradle执行一些我们自定义的操作。

Gradle

https://blog.csdn.net/zhaoyanjun6/article/details/76408024

自定义Gradle Plugin

https://cloud.tencent.com/developer/article/1372197

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值