返回顶部
首页 > 资讯 > 移动开发 >【Gradle-3】Gradle中的DSL,Groovy & Kotlin
  • 286
分享到

【Gradle-3】Gradle中的DSL,Groovy & Kotlin

kotlinandroid开发语言gradle 2023-09-06 09:09:06 286人浏览 泡泡鱼
摘要

1、前言 Gradle是一个构建工具,面向开发者的脚本语言是Groovy和Kotlin,即我们常用的build.gradle和build.gradle.kts或plugin等。 那么在Gradle 5

1、前言

Gradle是一个构建工具,面向开发者的脚本语言是GroovyKotlin,即我们常用的build.gradle和build.gradle.kts或plugin等。
那么在Gradle 5.0之后已经支持Kotlin的情况下,为什么还要讲Groovy,直接上Kotlin不行吗?
先来看一个图:
WX20221108-222113.png
这是Gradle使用的编程语言占比,排在第一的是Groovy,虽然有一部分是测试代码,但也说明groovy还是主流,
其次,在我们新建项目的时候,Groovy依然是默认的构建脚本语言;而且,截至目前依然有很多公司很多项目并没有迁移到Kotlin。所以在当下,Groovy依然是Gradle不得不提的官方构建脚本语言

在gradle中,有大量的配置是通过脚本语言来编写的,不管是Groovy还是Kotlin,最后的表现都是DSL,所以抛开编程语言不讲,DSL你也是逃不掉的。

本文主要介绍的内容:

  1. 什么是DSL;
  2. Groovy DSL & Kotlin DSL;
  3. Groovy 基础语法;

2、什么是DSL

DSL全称:Domain Specific Language,即领域特定语言,它是编程语言赋予开发者的一种特殊能力,通过它我们可以编写出一些看似脱离其原始语法结构的代码,从而构建出一种专有的语法结构。

DSL分为两类,外部DSL和内部DSL。

2.1、外部DSL

也称独立DSL。因为它们是从零开始建立起来的独立语言,而不基于任何现有宿主语言的设施建立。外部DSL是从零开发的DSL,在词法分析、解析技术、解释、编译、代码生成等方面拥有独立的设施。开发外部DSL近似于从零开始实现一种拥有独特语法和语义的全新语言。构建工具make 、语法分析器生成工具YACC、词法分析工具LEX等都是常见的外部DSL。例如:正则表达式、XML、sqlJSONmarkdown等;而大厂一般自研的动态化方案就是基于外部DSL来做的,从上传下发,到端侧解析渲染,从而实现一种不依赖发版且基于原生的动态化方案。

2.2、内部DSL

也称内嵌式DSL。因为它们的实现嵌入到宿主语言中,与之合为一体。内部DSL将一种现有编程语言作为宿主语言,基于其设施建立专门面向特定领域的各种语义。例如:Groovy DSL、Kotlin DSL等;简而言之可以理解为,这种DSL简化了原有的语法结构(看起来有点像lambda),这种简化大大的提高了简洁性,但也增加了理解成本,Gradle中的很多配置都是如此,这也是Gradle上手难的原因之一。

3、Gradle中的DSL

3.1、Groovy DSL

新建项目时Gradle默认的是Groovy语言,也即Groovy DSL,比如app>build.gradle:

plugins {    id 'com.Android.application'    id 'org.jetbrains.kotlin.android'}android {    namespace 'com.yechaoa.gradlex'    compileSdk 32    defaultConfig {        applicationId "com.yechaoa.gradlex"        minSdk 23        targetSdk 32        versionCode 1        versionName "1.0"    }}dependencies {    implementation 'androidx.core:core-ktx:1.7.0'}

我们来看第一段代码:

plugins {    id 'com.android.application'    id 'org.jetbrains.kotlin.android'}

这是一段用于声明引入Gradle插件plugin的DSL代码。
上面的plugin声明是简写版,完整版是这样的:

plugins {    id 'com.android.application' version '7.3.0' apply false}

3.1.1、id

调用的是PluginDependenciesSpec中的id(String id)函数,返回PluginDependencySpec对象,PluginDependencySpec对象可以理解为是PluginDependenciesSpec的一层封装,比如id(String id)函数只有一个参数,那versionapply哪里来的呢,就是在PluginDependencySpec对象里的。

注意:PluginDependenciesSpec和PluginDependencySpec长得像,但不一样的。

3.1.2、version

插件版本号。Gradle默认插件是不需要指定版本号的,但是三方的插件则必须指定版本号。很好理解,自带插件是可以跟着Gradle版本一起发布的,三方插件则是发布到远端仓库的,比如Gradle仓库gradlePluginPortal(),http://plugins.gradle.org/

3.1.3、apply

是否将插件应用于当前项目及子项目,默认是true
其实apply关键字就是用于管理依赖传递的。当我们不想传递时,就可以设置apply false

除此之外,还可以用表达式来管理依赖传递,比如这么写:

subprojects {    if (isNeedApply) {        apply plugin: "org.company.myplugin"    }}

3.2、还原DSL

介绍完plugin的声明之后,再回到plugins这段DSL代码

plugins {    id 'com.android.application'}

我们可以理解plugins是一个函数,plugins { } 里面接收的是一个闭包Closure,也可以这么写:

plugins ({    id 'com.android.application'})

在Groovy中,当函数的最后一个参数或者只有一个参数是闭包时,是可以写在参数括号外面的,所以还可以继续还原:

plugins () {    id 'com.android.application'}

在Groovy中,当函数只有一个参数的时候,是可以省略括号的,所以就回到了我们最初的DSL代码:

plugins {    id 'com.android.application'}

3.3、Kotlin写法

还是以plugins为例,来看看Kotlin是怎么写的。
简写版:

plugins {//    id 'com.android.application'    id("com.android.application")}

完整版:

plugins {//    id 'com.android.application' version '7.3.0' apply false    id("com.android.application") version "7.3.0" apply false}

我们可以看出来,其实Kotlin DSLGroovy DSL的写法差别不大,也是调用PluginDependenciesSpec中的id(String id)函数,只不过差别在调用id(String id)函数时有显式的括号而已。

4、闭包

上面的还原DSL其实涉及到Groovy里面一个非常重要的概念,闭包(Closure)。
闭包的展现形式跟Kotlin里面的lambda不能说非常相似吧,只能说是一毛一样。(kt吸收了很多精华)

4.1、定义闭包

我们先简单定义一个闭包:

// Closuredef myClosure = {}println(myClosure)

{ } 这个就是闭包,里面是空的什么都没做,所以打印出来什么也没有。
这个闭包对象可以理解为是一个函数,函数是可以接收参数的,我们加个参数改造一下:

// Closuredef myClosure = { param ->    param + 1}println(myClosure(1))

加了一个param参数,并在闭包里执行 +1 操作,然后打印这个闭包的时候传参为 1 。
输出:

> // Closure> def myClosure = { param ->>     param + 1> }> println(myClosure(1))2

打印结果:2。

4.2、Kotlin DSL

看到这里,会Kotlin的同学肯定有感受,这跟Kotlin函数参数简直太像了。
我们看一下Kotlin的函数参数:

private fun setPrintln(doPrintln: (String) -> Unit) {    doPrintln.invoke("yechaoa")}fun main() {    setPrintln { param ->        println(param)    }}
  1. 定义了一个高阶函数setPrintln,参数doPrintln是一个函数参数,String表示函数参数doPrintln的参数类型,Unit表示doPrintln不需要返回值。
  2. 然后doPrintln执行并传参"yechaoa"。

setPrintln函数调用实际上是这样的:

    setPrintln {            }

setPrintln函数的这个{ },跟上面的闭包一毛一样,就也称为闭包吧(lambda,其实也是DSL语法)。
然后我们在Kotlin的这个闭包里执行了一行代码:

println(param)

这个param就是doPrintln函数传的"yechaoa"参数。
然后我们看下输出:

yechaoaProcess finished with exit code 0

4.3、Groovy 闭包参数

对比完,我们再次回到Groovy的闭包。
上面我们定义了一个参数param,其实一个参数定义的时候是可以省略显示定义的,用it表示:

// Closuredef myClosure = {    it + 1}println(myClosure(1))

顺便来看下两个参数的:

// Closuredef myClosure = { param, param2 ->    param + 1 + param2}println(myClosure(1, 1))

输出:

> // Closure> def myClosure = { param, param2 ->>     param + 1 + param2> }> println(myClosure(1, 1))3

其实跟一个参数的方式没差别,跟Kotlin的写法也一样。

4.4、闭包函数参数

来看下当闭包作为函数参数时是怎样的表现:

def myClosure(Closure closure) {    println closure()}myClosure {    "yechaoa"}
  1. 定义了一个myClosure函数,参数是一个闭包,然后在函数里面对闭包进行打印;
  2. 直接调用这个函数,在闭包 { } 里传了一个字符串"yechaoa"。

我们这里的打印是println closure(),而不是println(closure())了,在Groovy DSL中,不产生歧义的情况下是可以去掉括号的,这也是我们前面提到的Groovy DSL和Kotlin DSL的区别,即id 'com.android.application’和id(“com.android.application”)的差别。

好了,看下输出:

> def myClosure(Closure closure) {>     println closure()> }> > myClosure {>     "yechaoa"> }yechaoa

到这里,有没有发现,这里的myClosure { }是不是跟plugins { }很像,不对,简直一毛一样。

我们在前文(【Gradle-2】一文搞懂Gradle配置)中的源码分析部分也有提到,Gradle中的配置块其实都是闭包调用。
myClosure { }就如同plugins { },"yechaoa"如同plugins { }里面的配置,myClosure(Closure closure)函数里面可以获取到"yechaoa"并打印出来,Gradle Project对象同样可以获取到plugins { }里面依赖的插件,然后解析执行编译构建。

通过闭包演示,是不是对Gradle的DSL理解轻松很多了~(别跑,点个赞)

以上Groovy演示代码都可以在 IDEA>Tools>Groovy Console 里执行测试。

5、Groovy

5.1、简介

Groovy是Apache 旗下的一种强大的、可选类型的和动态的语言,具有静态类型和静态编译功能,用于Java平台,旨在通过简洁、熟悉和易于学习的语法提高开发人员的生产力。它与任何Java程序顺利集成,并立即为您的应用程序提供强大的功能,包括脚本功能、领域特定语言创作、运行时和编译时元编程以及函数编程。
并且,Groovy可以与Java语言无缝衔接,可以在Groovy中直接写Java代码,也可以在Java中直接调用Groovy脚本,非常丝滑,学习成本很低。

5.2、基础语法

5.2.1、注释

注释和 Java 一样,支持单行//、多行和文档注释
不同的是Groovy在脚本里的注释用#

5.2.2、关键字

asassertbreakcasecatchclassconstcontinuedefdefaultdoelseenumextendsfalsefinallyforGotoifimplementsimportininstanceofinterfacenew、null、packagereturnsuperswitchthisthrowthrowstraittruetrywhile
asin、permitsrecord、sealed、trait、var、yields
null、truefalsebooleancharbyteshortintlongfloatdouble

5.2.3、字符串

在Groovy种有两种字符串类型,普通字符串java.lang.String和插值字符串groovy.lang.GString
普通字符串:

println 'yechaoa'

插值字符串:

def name = 'yechaoa'println "hello ${name}"// orprintln "hello $name"

5.2.4、数字

与java类型相同:

  • byte(8位)
  • char(16位,可用作数字类型,表示UTF-16代码)
  • short(16位)
  • int(32位)
  • long(64位)
  • java.math.BigInteger(2147483647个int)

可以这么声明:

byte  b = 1char  c = 2short s = 3int   i = 4long  l = 5BigInteger bi =  6

类型会根据数字的大小调整。

def a = 1assert a instanceof Integer// Integer.MAX_VALUEdef b = 2147483647assert b instanceof Integer// Integer.MAX_VALUE + 1def c = 2147483648assert c instanceof Long// Long.MAX_VALUEdef d = 9223372036854775807assert d instanceof Long// Long.MAX_VALUE + 1def e = 9223372036854775808assert e instanceof BigInteger

5.2.5、变量

Groovy提供了def关键字,跟Kotlin的var一样,属于类型推导。

def a = 123def b = 'b'def c = true boolean d = falseint e = 123

但Groovy其实是强类型语言,但是与Kotlin一样,也支持类型推导。
下面这几种定义也都是ok的

def a1 = "yechaoa"String a2 = "yechaoa"a3 = "yechaoa"println(a3)

5.2.6、运算符

算术运算符

assert  1  + 2 == 3assert  4  - 3 == 1assert  3  * 5 == 15assert  3  / 2 == 1.5assert 10  % 3 == 1assert  2 ** 3 == 8

一元运算符

def a = 2def b = a++ * 3             assert a == 3 && b == 6def c = 3def d = c-- * 2             assert c == 2 && d == 6def e = 1def f = ++e + 3             assert e == 2 && f == 5def g = 4def h = --g + 1             assert g == 3 && h == 4

赋值运算符

def a = 4a += 3assert a == 7def b = 5b -= 3assert b == 2def c = 5c *= 3assert c == 15def d = 10d /= 2assert d == 5def e = 10e %= 3assert e == 1def f = 3f **= 2assert f == 9

关系运算符

assert 1 + 2 == 3assert 3 != 4assert -2 < 3assert 2 <= 2assert 3 <= 4assert 5 > 1assert 5 >= -2

逻辑运算符

assert !false           assert true && true     assert true || false  

位移运算符

assert 12.equals(3 << 2)           assert 24L.equals(3L << 3)         assert 48G.equals(3G << 4)         assert 4095 == -200 >>> 20assert -1 == -200 >> 20assert 2G == 5G >> 1assert -3G == -5G >> 1

5.2.7、List

Groovy用大括号表示列表,参数用逗号分割。

def numbers = [1, 2, 3]         assert numbers instanceof List  assert numbers.size() == 3  

除了可以定义同类型之外,也可以定义不同类型值的列表

def heterogeneous = [1, "a", true] 

迭代通过调用eacheachWithIndex方法,与Kotlin类似

[1, 2, 3].each {    println "Item: $it"//it是对应于当前元素的隐式参数}['a', 'b', 'c'].eachWithIndex { it, i -> //it是当前元素, i是索引位置    println "$i: $it"}

5.2.8、Arrays

跟List差不多,不一样的是 需要显示声明类型

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  assert arrStr instanceof String[]    assert !(arrStr instanceof List)def numArr = [1, 2, 3] as int[]      assert numArr instanceof int[]       assert numArr.size() == 3

5.2.9、Map

key-value映射,每一对键值用冒号:表示,对与对之间用逗号分割,最外层是中括号。

def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   assert colors['red'] == '#FF0000'    assert colors.green  == '#00FF00'    colors['pink'] = '#FF00FF'           colors.yellow  = '#FFFF00'           assert colors.pink == '#FF00FF'assert colors['yellow'] == '#FFFF00'assert colors instanceof java.util.LinkedHashMap

5.2.10、闭包

Groovy 提供了闭包的支持,语法和 Lambda 表达式有些类似,简单来说就是一段可执行的代码块或函数指针。闭包在 Groovy 中是groovy.lang.Closure类的实例,这使得闭包可以赋值给变量,或者作为参数传递。
Groovy 定义闭包的语法很简单:

{ [closureParameters -> ] statements }

尽管闭包是代码块,但它可以作为任何其他变量分配给变量或字段:

def listener = { e -> println "Clicked on $e.source" }      assert listener instanceof ClosureClosure callback = { println 'Done!' }                      Closure<Boolean> isTextFile = {    File it -> it.name.endsWith('.txt')                     }

闭包可以访问外部变量,而函数则不能。

def str = 'yechaoa'def closure={    println str}closure()//yechaoa 

闭包调用的方式有两种,闭包.call(参数)或者闭包(参数),在调用的时候可以省略圆括号。

def closure = {    param -> println param} closure('yechaoa')closure.call('yechaoa')closure 'yechaoa'

闭包的参数是可选的,如果没有参数的话可以省略->操作符。

def closure = {println 'yechaoa'}closure()

多个参数以逗号分隔,参数类型和函数一样可以显式声明也可省略。

def closure = { String x, int y ->        println "hey ${x} the value is ${y}"}

如果只有一个参数的话,也可省略参数的定义,Groovy提供了一个隐式的参数it来替代它。类似Kotlin。

def closure = { it -> println it } //和上面是等价的def closure = { println it }   closure('yechaoa')

闭包可以作为参数传入,闭包作为函数的唯一参数或最后一个参数时可省略括号。

def eachLine(lines, closure) {    for (String line : lines) {        closure(line)    }}eachLine('a'..'z',{ println it }) //可省略括号,与上面等价eachLine('a'..'z') { println it }

类似kotlin的高阶函数。

再举个常见的例子,我们经常用到的dependencies

dependencies {    testImplementation 'junit:junit:4.13.2'}

这个dependencies就是一个闭包,等价于

dependencies ({    testImplementation 'junit:junit:4.13.2'})

继续等价于

dependencies ({    testImplementation('junit:junit:4.13.2')})

没错,testImplementation也是闭包,所以现在我们在kotlin中也可以经常见到implementation(‘xxx’)这种写法。

5.2.11、IO操作

5.2.11.1、读文件

读取文本文件并打印每一行文本

new File(baseDir, 'yechaoa.txt').eachLine{ line ->    println line}

InputStream

new File(baseDir,'yechaoa.txt').withInputStream { stream ->    // do something ...}

5.2.11.1、写文件

new File(baseDir,'yechaoa.txt').withWriter('utf-8') { writer ->    writer.writeLine 'Into the ancient pond'    writer.writeLine 'A frog jumps'    writer.writeLine 'Water’s sound!'}

OutputStream

new File(baseDir,'data.bin').withOutputStream { stream ->    // do something ...}

5.2.12、小结

Groovy整体语法特性与java、kotlin都有相似之处,比较容易上手。

当然,也可以用kotlin开发,kotlin相比groovy在开发上也有不少优势,比代码补全、支持跳转、显示注释等。
从groovy迁移到kotlin可以参考两篇官方的文档:

6、总结

本文先是介绍了什么是DSL,然后对比了Groovy DSL 和 Kotlin DSL语法上的一些差异,以及闭包的讲解,最后简单介绍了一下Groovy的基础语法。核心是DSL和闭包的概念,希望本文对你有所帮助~

写作不易,点个赞吧~

7、相关文档

来源地址:https://blog.csdn.net/yechaoa/article/details/130257106

--结束END--

本文标题: 【Gradle-3】Gradle中的DSL,Groovy & Kotlin

本文链接: https://lsjlt.com/news/396843.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作