Android Gradle开发与应用 (八) :Kotlin DSL

慈云数据 1年前 (2024-04-19) 技术支持 58 0

1. 前言

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

Android Gradle开发与应用 (八) :Kotlin DSL
(图片来源网络,侵删)

2. DSL是什么

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

2.1 举例说明

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

Android Gradle开发与应用 (八) :Kotlin 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 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().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().message.set("Hi from Gradle")
        

        在 Kotlin DSL 中,由于 Kotlin 是一种静态类型语言,它提供了更严格的类型检查和更丰富的类型系统。因此,访问插件的扩展对象需要使用 the() 函数,这个函数是 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, 但它们之间存在一些区别:

        1. 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
            }
        }
        

        7. 其他

        gradle中Kotlin和Groovy的差异 :

        将 build 配置从 Groovy 迁移到 Kotlin | Android Studio | Android Developers (google.cn)

        Gradle Kotlin DSL 入门

        Android Gradle系列文章

        Android Gradle 开发与应用 (一) : Gradle基础-氦客-CSDN博客

        Android Gradle开发与应用 (二) : Groovy基础语法-CSDN博客

        Android Gradle开发与应用 (三) : Groovy语法概念与闭包-CSDN博客

        Android Gradle开发与应用 (四) : Gradle构建与生命周期-CSDN博客

        Android Gradle开发与应用 (五): 基于Gradle 8.2,创建Gradle插件-CSDN博客

        Android Gradle 开发与应用 (六) : 创建buildSrc插件和使用命令行创建Gradle插件-CSDN博客

        Android Gradle 开发与应用 (七) : 实现打包自动复制文件插件

        Android Gradle开发与应用 (八) :Kotlin DSL

微信扫一扫加客服

微信扫一扫加客服