Android Gradle开发、应用、插件发布(五)—Kotlin DSL

1. 前言

本文介绍了Gradle Kotlin DSL相关的一些知识点

2. DSL是什么

DSL是为特定领域设计的专门的语言,也就是设计了一门语言,然后解决某个特定的领域的特定问题。

2.1 举例说明

以下的这些都可以称之为DSL

  • 正则表达式 :用于文本处理的特定语言
  • SQL :用于数据库查询的领域特定语言
  • HTML : 用于描述网页结构的领域特定语言
  • CSS:用于描述网页样式的领域特定语言
  • Android XML布局 : 用于描述Android页面的领域特定语言

而Gradle 中的 Groovy DSL 和 Kotlin DSL 就用来编写Gradle构建脚本的领域特定语言

像Kotlin它不仅可以做一些Android开发,还可以做后端,它不属于DSL,但是我们可以通过Kotlin的一些语法规则,来开发一些特定领域的DSL语言。

2.2 Kotlin DSL

Kotlin当中的DSL,使用高阶函数和一些Lambda表达式配合来进行编写的。

对于DSL没有明确的语法规定,只要能编写出一门语言,一种语法规则,解决特定领域的问题,那么他就可以称之为DSL。

Gradle构建工具现在也使用Kotlin作为Gradle的主要编写语言了。

我们去创建Kotlin的Gradle的时候,可以发现,它里面的内容和以前的Gradle没什么区别,但是语言上是用的Kotlin。

其实就是用Kotlin写了一个专门为Gradle使用的DSL。

3. kotlin-dsl是什么

在用Kotlin编写的Gradle插件中,会有kotlin-dsl这一句

plugins {
    `java-gradle-plugin`
    `kotlin-dsl`
}

我们点进去可以看到kotlin-dsl中间的-不是标准的kotlin语法,所以要加上``来做区分。

public val val PluginDependenciesSpec.`kotlin-dsl`: PluginDependencySpec
    get() = id("org.gradle.kotlin.kotlin-dsl") version "2.4.1"


也就是说 kotlin-dsl就是PluginDependencySpec的一个扩展属性。
其实就是相当于id("org.gradle.kotlin.kotlin-dsl") version "2.4.1"

所以如果把kotlin-dsl这行代码改为id("org.gradle.kotlin.kotlin-dsl") version "2.4.1",效果是一样的。

可以在Kotlin插件项目里,全局搜索org.gradle.kotlin.kotlin-dsl

3.1 kotlin-dsl插件的作用

提供Kotlin编写Gradle脚本的能力,从而可以替代传统的Groovy语言。

使用Kotlin DSL的编程风格,可以使程序更加简单干净、直观简洁。这有助于提升开发效率,降低出错的可能性,并使得构建脚本更易于理解和维护。

此外,Kotlin DSL还提供了强大的类型检查和智能代码补全功能,这些功能可以进一步提高开发者的编写体验。同时,由于Kotlin语言的强大功能,Kotlin DSL也支持更复杂的构建逻辑和更灵活的脚本编写方式。

4. groovy和kotlin的语法糖

4.1 apply plugin
4.1.1 groovy中的apply plugin

apply plugin : MyPlugin

等价于

apply([plugin : MyPlugin])

当方法的参数是一个map的时候,可以将方括号[]去掉

apply(plugin: MyPlugin)

当不引起歧义的时候,可以把圆括号去掉

apply plugin : MyPlugin
 

4.1.2 kotlin中的apply plugin

apply(plugin = "com.android.application")

实际上也是个函数调用

public fun org.gradle.api.plugins.PluginAware.apply(from: kotlin.Any? = COMPILED_CODE, plugin: kotlin.String? = COMPILED_CODE, to: kotlin.Any? = COMPILED_CODE): kotlin.Unit {  }


4.2 gradle和kotlin中的plugins
4.2.1 groovy中的plugins

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

本质是有一个plugins的方法,调用了一个闭包
实质上是

plugins({
    id('com.android.application').version('8.1.3').apply(false)
})


4.2.2 kotlin中的plugins

plugins {
    id("com.android.application") version "8.1.3" apply false
}

本质上也就是一个函数调用

public final fun plugins(block: org.gradle.kotlin.dsl.PluginDependenciesSpecScope.() -> kotlin.Unit): kotlin.Unit { /* compiled code */ }

id方法会返回一个PluginDependencySpec,PluginDependencySpec中有version和apply方法

public interface PluginDependencySpec {
    PluginDependencySpec version(@Nullable String version);

    default PluginDependencySpec version(Provider<String> version) {
        return this.version((String)version.get());
    }

    PluginDependencySpec apply(boolean apply);
}

4.2.3 apply plugin 和 plugins的区别
  • plugins是Gradle较新的方式,用于声明性地应用插件。有更简洁的语法,更好的插件版本控制
  • 传统的apply方法机制则更加灵活,可以动态地加载和应用插件

现在一般情况下都用plugins即可,除非是要使用build.gradle中自己写的gradle插件,采用apply plugin进行引用。

5. Groovy DSL 和 Kotlin DSL的区别

5.1 gradle中,如下Groovy和Kotlin的写法为什么会有不同 ?

比如下面的这行代码 greeting.message = 'Hi from Gradle' 和 the<GreetingPluginExtension>().message.set("Hi from Gradle")

在 Gradle 中,Groovy DSL 和 Kotlin DSL 提供了不同的语法和风格来配置构建脚本。这两种语言不同的写法来配置插件扩展,是由于这两种 DSL 在语法和设计哲学上的差异造成的。

首先,来看 Groovy DSL 的写法:

greeting.message = 'Hi from Gradle'

在 Groovy DSL 中,插件的扩展对象通常会被添加到项目的 extensions 集合中,并且可以直接通过扩展对象的名称(在这个例子中是 greeting)来访问。Groovy 语言允许直接对属性进行赋值操作,因此可以非常方便地通过点号 . 操作符来设置属性的值。

接下来,看 Kotlin DSL 的写法:

the<GreetingPluginExtension>().message.set("Hi from Gradle")

在 Kotlin DSL 中,由于 Kotlin 是一种静态类型语言,它提供了更严格的类型检查和更丰富的类型系统。因此,访问插件的扩展对象需要使用 the<T>() 函数,这个函数是 Gradle Kotlin DSL 提供的一个帮助函数,用于获取指定类型的扩展对象。这样做可以提供更好的类型安全,并且使代码更加清晰和易于理解。

另外,set 方法的使用也是 Kotlin 语言特性的体现。在 Kotlin 中,属性默认是不可变的(val),如果需要修改属性的值,通常需要提供一个 set 方法。虽然 Kotlin 也支持可变属性(var),但在 Gradle Kotlin DSL 的上下文中,使用 set 方法可能是为了遵循 Gradle API 的约定,或者是为了强调这是一个设置操作。

总结来说,Groovy DSL 和 Kotlin DSL 在语法和风格上的差异导致了配置插件扩展时的不同写法。Groovy DSL 提供了更加直接和简洁的语法,而 Kotlin DSL 则强调了类型安全和清晰的代码结构。这些差异使得两种 DSL 都有其独特的优点,开发者可以根据个人喜好和项目需求选择使用哪一种。

5.2 gradle中,tasks.register(‘xxx’)和task xxx这两种新建Task方式,有什么区别 ?

在Gradle中,tasks.register('xxx')和task xxx这两种方式用于新建Task, 但它们之间存在一些区别:

Groovy 和 Kotlin 语法:

task xxx 是 Groovy 语法,它使用 Groovy 闭包来配置任务。
tasks.register('xxx') 是 Kotlin 语法,它通过流式 API 调用来配置任务。

任务创建时机:

task xxx 是在配置文件(如 build.gradle)执行期间立即创建任务的。
tasks.register('xxx') 可以延迟任务的创建,直到需要时才注册到任务注册表中。
可读性和维护性:

task xxx 的语法更接近传统的 Gradle 构建脚本,对于熟悉早期版本的 Gradle 的用户来说可能更易读。
tasks.register('xxx') 提供了一种更一致和流畅的任务配置方式,与 Gradle 推向的 Kotlin DSL 相契合。

API 兼容性:

task xxx 的语法可能会在未来的 Gradle 版本中被弃用,因为 Gradle 正在推动使用 Kotlin DSL。
tasks.register('xxx') 是当前推荐的方式,因为它与 Gradle 的 Kotlin DSL 兼容,并且有更好的未来兼容性保证。

任务名称:

task xxx 中的任务名称(‘xxx’)通常不包含引号。
tasks.register('xxx') 中的任务名称必须包含引号。

示例:

使用 task xxx 语法创建一个名为 ‘myTask’ 的任务:

task myTask {
    doLast {
        println 'Hello from myTask!'
    }
}

使用 tasks.register('xxx') 语法创建同样的任务:

tasks.register('myTask') {
    doLast {
        println 'Hello from myTask!'
    }
}

总的来说,虽然 task xxx 和 tasks.register('xxx') 都可以用来创建新的任务,但是 tasks.register 是更现代、更灵活且未来兼容的方式。随着 Gradle 的发展,推荐使用 Kotlin 语法和 tasks.register 方法进行任务创建和配置。

6. 自定android闭包

使用Kotlin DSL模拟android闭包来实现一个myandroid的闭包,将MyAndroidBean传入。

首先创建一个MyAndroidBean和DefaultConfig 传参类

class MyAndroidBean {
    var namespace = ""
    var compileSdk = 0
    var myDefaultConfig = DefaultConfig()

    fun Project.myDefaultConfig(call: DefaultConfig.() -> Unit) {
        val defaultConfig = DefaultConfig()
        defaultConfig.call()
    }
}

class DefaultConfig {
    var applicationId = ""
    var minSdk = 0
    var targetSdk = 0
}

然后实现一个自定义的myandroid闭包

fun Project.myandroid(call: MyAndroidBean.() -> Unit) {
    val myAndroidBean = MyAndroidBean()
    myAndroidBean.call()
    val defaultConfig = myAndroidBean.myDefaultConfig
    //打印传参
    println("namespace:${myAndroidBean.namespace}")
    println("namespace:${myAndroidBean.compileSdk}")
    println("applicationId:${defaultConfig.applicationId}")
    println("minSdk:${defaultConfig.minSdk}")
    println("targetSdk:${defaultConfig.targetSdk}")
}

进行调用

myandroid {
    namespace = "com.heiko.mytest"
    compileSdk = 34

    myDefaultConfig {
        applicationId = "com.heiko.mytest"
        minSdk = 21
        targetSdk = 34
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值