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`: PluginDependencySpecget() = 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 = 0var myDefaultConfig = DefaultConfig()fun Project.myDefaultConfig(call: DefaultConfig.() -> Unit) {val defaultConfig = DefaultConfig()defaultConfig.call()}
}class DefaultConfig {var applicationId = ""var minSdk = 0var 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 = 34myDefaultConfig {applicationId = "com.heiko.mytest"minSdk = 21targetSdk = 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
相关文章:
Android Gradle开发与应用 (八) :Kotlin DSL
1. 前言 本文介绍了Gradle Kotlin DSL相关的一些知识点 2. DSL是什么 DSL是为特定领域设计的专门的语言,也就是设计了一门语言,然后解决某个特定的领域的特定问题。 2.1 举例说明 以下的这些都可以称之为DSL 正则表达式 :用于文本处理的特定语言SQ…...
phpstorm 快捷键
PHPstorm最常用的快捷键,提高开发效率 - 知乎 (zhihu.com) 四年精华PHP技术文章整理合集——PHP框架篇 (qq.com) 四年精华PHP技术文合集——微服务架构篇 (qq.com) Vue3 打印票据 预览的库:vue3打印解决方案:Vue-Plugin-HiPrint - 掘金 (j…...
浦大喜奔APP8.0智能升级,发力数字金融深化五大金融篇章服务
1. 浦大喜奔立足科技赋能持续迭代升级,筑牢用户体验护城河 浦发信用卡中心坚持数字科技与客户体验双轮驱动,以科技赋能发展,优化整体系统性能,全方位支撑浦大喜奔 APP提高线上客户服务能力与体验,积极服务民生消费&a…...
自然语言处理、大语言模型相关名词整理
自然语言处理相关名词整理 零样本学习(zero-shot learning)词嵌入(Embedding)为什么 Embedding 搜索比基于词频搜索效果好? Word2VecTransformer检索增强生成(RAG)幻觉采样温度Top-kTop-p奖励模…...
移动开发避坑指南——内存泄漏
在日常编写代码时难免会遇到各种各样的问题和坑,这些问题可能会影响我们的开发效率和代码质量,因此我们需要不断总结和学习,以避免这些问题的出现。接下来我们将围绕移动开发中常见问题做出总结,以提高大家的开发质量。本系列文章…...
太好玩了,我用 Python 做了一个 ChatGPT 机器人
毫无疑问,ChatGPT 已经是当下编程圈最火的话题之一,它不仅能够回答各类问题,甚至还能执行代码! 或者是变成一只猫 因为它实在是太好玩,我使用Python将ChatGPT改造,可以实现在命令行或者Python代码中调用。…...
STM32存储左右互搏 SDIO总线读写SD/MicroSD/TF卡
STM32存储左右互搏 SDIO总线读写SD/MicroSD/TF卡 SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元,由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡,手机领域用的TF卡实际就是MicroSD卡,尺寸比SD卡小,而电路和协…...
累积分布函数图(CDF)的介绍、matlab的CDF图绘制方法(附源代码)
在对比如下两个误差的时候,怎么直观地分辨出来谁的误差更低一点?: 通过这种误差时序图往往不容易看出来。 但是如果使用CDF图像,以误差绝对值作为横轴,以横轴所示误差对应的累积概率为纵轴,绘制曲线图&am…...
代码随想录算法训练营第四十一天|343.整数拆分、96不同的二叉搜索树
文档链接:https://programmercarl.com/ LeetCode343.整数拆分 题目链接:https://leetcode.cn/problems/integer-break/ 思路: j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘…...
全量知识系统 程序详细设计之 统一资产模型(QA-SmartChat)
Q1. 下面我们聊聊整个全知系统的设计 的矩阵和函数,矩阵表示的是“活物”,分别 类似 一个基因的活性、一个实体的辨识度和某种特征的可区分度。 函数的可微、可积和可导性 则表示 运动的控制方式 在全知系统设计中,矩阵和函数是两个核心的组…...
已解决org.springframework.web.client.HttpClientErrorException: 400异常的正确解决方法,亲测有效!!!
已解决org.springframework.web.client.HttpClientErrorException: 400异常的正确解决方法,亲测有效!!! 文章目录 问题分析 报错原因 解决思路 解决方法 总结 在日常开发过程中,通过Spring框架提供的RestTemplat…...
内网渗透-Windows内网渗透
内网渗透-Windows内网渗透 文章目录 内网渗透-Windows内网渗透前言一、信息收集 1.1、SPN1.2、端口连接1.3、配置文件1.4、用户信息1.6、会话收集1.7、凭据收集 navicat:SecureCRT:Xshell:WinSCP:VNC: 1.8、DPAPI1.9、域信任1.10、…...
机器人方向控制中应用的磁阻角度传感芯片
磁阻传感器提供的输出信号几乎不受磁场变动、磁温度系数、磁传感器距离与位置变动影响,可以达到高准确度与高效能,因此相当适合各种要求严格的车用电子与工业控制的应用。所以它远比采用其它传感方法的器件更具有优势。 机器人的应用日渐广泛࿰…...
如何在树莓派安装Nginx并实现固定公网域名访问本地静态站点
文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx(发音为“engine-x”)可以将您的树莓派变成一个强大的 Web 服务器,可以用于托管网站或 Web 应用程序。相比其他 Web 服务器,Ngi…...
Ubuntu与主机windows共享文件夹
一、创建共享文件夹: 虚拟机->设置->选项->共享文件夹->总是启用->选择本地的共享文件夹(如E:\Share)->确定。 二、设置挂载: 首先赋予/etc/fstab文件可编辑的权限; sudo chmod 777 /…...
(四)C++自制植物大战僵尸游戏启动流程
植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、启动方式 鼠标左键单机VS2022上方工具栏中绿色三角按钮(本地Windows调试器)进行项目启动。第一次启动项目需要编译项目中所有代码文件,编译生成需要一定的时间。不同性能的电…...
华为的AI战略地图上,才不是只有大模型
图片来源:pixabay© 钛媒体ToB深水区 图片来源:pixabay 大模型火热了一年,现在还没做AI化改造的企业,就像是工业革命浪潮伊始与火车赛跑的那辆马车。 最早的蒸汽火车缓慢又笨重,甚至铁轨上还预留了马匹行走的空…...
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
采用C#.Net JavaScript 开发的云LIS系统源码 二级医院应用案例有演示 一、系统简介 云LIS是为区域医疗提供临床实验室信息服务的计算机应用程序,可协助区域内所有临床实验室相互协调并完成日常检验工作,对区域内的检验数据进行集中管理和共享࿰…...
Vue3(三):生命周期、路由、自定义hooks
这里终于明白了为什么一直有这个语法报错,就是在提示你哪里错的地方上方注释一行/*eslint-disable*/,之前一直警告这个错误感谢老师! 一、vue2和vue3生命周期 还有一个问题就是父组件和子组件哪个先挂载完毕呢?答案是子组件先挂…...
UE4_导入内容_骨架网格体
FBX 导入支持 骨架网格体(Skeletal Mesh) 。这提供了一种简化的处理流程来将有动画的网格体从 3D应用程序中导入到虚幻引擎内,以便在游戏中使用。除了导入网格体外,如果需要,动画和变形目标都可以使用FBX格式 在同一文…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
