Android Gradle开发与应用 (三) : Groovy语法概念与闭包
1. Groovy介绍
Groovy是一种基于Java平台的动态编程语言,与Java是完全兼容,除此之外有很多的语法糖来方便我们开发。Groovy代码能够直接运行在Java虚拟机(JVM)上,也可以被编译成Java字节码文件。
以下是Groovy的一些特性:
- 简洁:
Groovy的语法比Java更加简洁,可以用更少的代码完成同样的功能。 - 动态语言:
Groovy是一种动态语言,支持动态类型和动态方法调用等特性,这意味着你可以在编写代码时不必指定变量的类型。 - 完全兼容Java:
Groovy可以无缝使用Java的所有类库,也可以直接在Groovy代码中使用Java代码。

2. Groovy运行机制
Groovy是一种基于Java虚拟机(JVM)的面向对象编程语言,其运行机制主要包括以下几个方面:
-
解析阶段:Groovy代码首先会被Groovy编译器解析为一个抽象语法树(AST)。AST是源代码的图形化表示,它以树状的形式描绘出源代码的结构,使编译器能够更好地理解和处理代码。
-
编译阶段:在AST生成后,Groovy编译器会将它转换为Java字节码。这是因为Groovy是一种运行在JVM上的语言,必须将源代码转换为Java字节码,才能被JVM执行。
-
运行阶段:生成的Java字节码最后会被JVM加载并执行。在这个过程中,如果Groovy代码中包含了动态类型,那么Groovy会在运行时进行类型检查和方法调用的解析。
-
动态语言的特性:作为一种动态语言,Groovy的一大特性就是它的动态性。它支持动态方法调用,即在运行时解析方法调用,而不是在编译时。这使得Groovy在处理一些特定问题时更加灵活,例如处理JSON和XML等数据格式。
- 可以想象成纯反射的调用,加上元编程的特性,使Groovy可以在运行时解析方法调用
- 除非加上
@CompileStatic会按照Java的方式静态编译,否则都是动态编译的
-
元编程:Groovy还支持元编程,它允许开发者在运行时修改类的结构或行为。这使得Groovy可以实现一些强大的功能,例如创建DSL(领域特定语言)、添加或修改类的方法等。
-
脚本执行:Groovy还可以作为脚本语言使用,即不需要进行编译,直接运行Groovy代码。在脚本模式下,Groovy会使用一个特殊的类加载器来解析和执行代码。
Groovy的运行机制深度整合了编译型语言和解释型语言的优势,既拥有编译型语言的性能优势,又保留了解释型语言的灵活性和便利性。
3. Groovy DSL
本身Groovy DSL的目标就是成为一个通用的DSL语言,所以在Groovy中,方法调用可以不写括号
比如 :
turn(left).then(right)可以简写为turn left then righttake(2.pills).of(chloroquinine).after(6.hours)可以简写为take 2.pills of chloroquinine after 6.hourspaint(wall).with(red, green).and(yellow)可以简写为paint wall with red, green and yellowcheck(that: margarita).tastes(good)可以简写为check that: margarita tastes goodgiven({}).when({}).then({})可以简写为given { } when { } then { }
具体详见 Groovy DSL
3.1 Groovy DSL 示例一
比如我们在Android项目中经常可以看到这样一行代码
apply plugin: MyPlugin
这行代码等价于
apply([plugin : MyPlugin])
当方法的参数是一个map的时候,可以将方括号[]去掉
apply(plugin: MyPlugin)
当不引起歧义的时候,可以把圆括号去掉,从而得到了我们经常看到的这行代码
apply plugin : MyPlugin
3.2 Groovy DSL 示例二
在新版的Gradle中,默认情况下,已经不使用apply plugin了,而是使用plugins{}来引入插件了。
plugins {id 'com.android.application' version '8.1.3' apply false
}
本质是有一个plugins的方法,调用了一个id 'com.android.application' version '8.1.3' apply false的闭包
plugins({id('com.android.application').version('8.1.3').apply(false)
})
4. 闭包
4.1 最简单的闭包
先来看一个最简单的闭包
//声明一个闭包
def closure = {println "hello world!"//return 1
}//可以直接调用它,因为它就是一个函数
closure()
//等同于上面这行
closure.call()
4.2 带参数的闭包
带参数的闭包只需要传入需要的参数,声明闭包的时候,指明这个参数(比如param1)就好了
def closure = { param1 ->println("running start...:" + param1)println("running end...")
}//进行调用,并传参
closure("heiko")
//等同于上面这行
closure.call("qwerty")
打印的日志
running start...:heiko
running end...
running start...:qwerty
running end...
4.3 闭包在实际开发中的应用
4.3.1 无参数
一般在实际开发中,闭包是作为传参传入的,通过closure.call()进行回调
def closure(Closure closure){println("running start...")//closure() 这种调用方式也可以closure.call()println("running end...")
}
然后在调用方法的时候,就可以很方便的通过闭包{}进行调用了
closure {println("running........")
}
打印的日志如下
running start...
running........
running end...
4.3.2 有参数的情况
闭包有参数的情况,那么通过closure.call()传入了两个参数10和15
def calc(Closure closure) {//closure(10,15) 这种调用方式也可以def result = closure.call(10, 15)println("result:" + result)
}
那么在调用方法的时候,闭包可以声明v1,v2这两个参数,然后就可以直接使用了
calc { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
打印的日志如下
v1:10 v2:15
result:25
4.3.3 调用闭包的时候传参
调用方法的时候,我们可以传参,然后还可以将这个参数,回调给闭包closure.call(num1, num2)
def calc2(num1, num2, Closure closure) {//closure(num1,num2) 这种调用方式也可以def result = closure.call(num1, num2)println("result:" + result)
}
调用方法的时候,就是在()里多传入两个参数就好了
calc2(6, 7) { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
打印日志如下
v1:6 v2:7
result:13
4.4 闭包{}是怎么出现的
4.4.1 最初的闭包
def calc3(num1, num2, Closure closure) {//closure(num1,num2) 这种调用方式也可以def result = closure.call(num1, num2)println("result:" + result)
}
4.4.2 调用方法
闭包作为方法的最后一个参数的时候,可以写在方法外面
calc3(1, 2) { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
4.4.3 方法没有 其他参数的情况
如果方法没有其他参数的话,调用的时候是(),闭包{}在()外面
def calc3(Closure closure) {def result = closure.call(num1, num2)println("result:" + result)
}calc3() { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
4.4.4 省略大括号
方法调用的时候,在不引起歧义的情况下,大括号()也可以省略,这样就成为我们最终看到的闭包的样子了。
def calc3(Closure closure) {def result = closure.call(1, 2)println("result:" + result)
}calc3 { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
5. 写一个自己的android闭包
在Android项目,我们平时最常见的就是android这个闭包了,那么我们能不能自己写一个android闭包呢
android {namespace 'com.heiko.mytest'compileSdk 34defaultConfig {applicationId "com.heiko.mytest"minSdk 24targetSdk 34}
}
5.1 声明MyAndroidBean类
声明MyAndroidBean类,用来定义需要传递的参数
class MyAndroidBean {public String namespacepublic Integer compileSdk
}
5.2 声明函数 : myandroid
声明函数myandroid,传参为一个闭包closure,然后调用project.configure(myAndroidBean, closure)使闭包转化为MyAndroidBean,然后就可以调用myAndroidBean的属性了。
def myandroid(Closure closure) {MyAndroidBean myAndroidBean = new MyAndroidBean()project.configure(myAndroidBean, closure)println(myAndroidBean.namespace)println(myAndroidBean.compileSdk)
}
5.3 调用myandroid
接着写上这些代码,来调用myandroid,并配置了namespace和compileSdk的值
myandroid {namespace = "com.heiko.mm"compileSdk = 31
}
5.4 Sync下项目
然后我们Sync下项目,可以发现打印出了如下日志
myandroid {namespace = "com.heiko.mm"compileSdk = 31
}
5.5 声明MyDefaultConfig类
声明MyDefaultConfig类,用来定义mydefaultConfig闭包内的参数
class MyDefaultConfig {public String applicationIdpublic int minSdkpublic int targetSdk
}
5.6 声明函数 : mydefaultConfig
声明函数mydefaultConfig,传参为一个闭包closure,然后调用closure.delegate = config。closure.delegate = defaultConfig这行代码的作用是将闭包的委托对象设置为defaultConfig实例。这意味着在闭包内部,当你尝试访问或设置一个属性(如applicationId、minSdk或targetSdk)时,实际上是在defaultConfig对象上执行这些操作。
class MyAndroidBean {public String namespacepublic Integer compileSdkpublic MyDefaultConfig defaultConfigdef mydefaultConfig(Closure closure) {MyDefaultConfig config = new MyDefaultConfig()closure.delegate = configclosure.call()defaultConfig = config}
}def myandroid(Closure closure) { // 添加project参数MyAndroidBean myAndroidBean = new MyAndroidBean()closure.delegate = myAndroidBeanclosure.call()println("namespace:" + myAndroidBean.namespace)println "compileSdk:" + (myAndroidBean.compileSdk)println "applicationId:" + (myAndroidBean.defaultConfig.applicationId)println "minSdk:" + (myAndroidBean.defaultConfig.minSdk)println "targetSdk:" + (myAndroidBean.defaultConfig.targetSdk)
}
在Groovy中,闭包(Closure)是一种可以引用和使用其周围环境中的变量的代码块。闭包有三种重要的属性:delegate、owner和this。
delegate属性是执行闭包时用于解析方法调用和属性引用的对象。也就是说,当你在闭包内部调用一个方法或引用一个属性,Groovy会首先在delegate对象上查找这个方法或属性。如果在delegate对象上找不到,它将在owner和this对象上查找。
默认情况下,delegate对象是owner对象,但你可以自由地改变它。当你设置了一个新的delegate,你可以在闭包中引用和操作这个新对象的方法和属性,就像它们是在闭包内部定义的一样,这个特性使得你可以在闭包中使用DSL样式的代码。
5.7 调用mydefualtConfig
这个时候就可以去调用mydefaultConfig方法了,并可以对applicationId、minSdk、targetSdk属性进行配置。
myandroid {namespace = "com.heiko.mm"compileSdk = 31mydefaultConfig {applicationId = "com.heiko.mm"minSdk = 21targetSdk = 31}
}
最后Sync下项目,可以看到打印日志如下
namespace:com.heiko.mm
compileSdk:31
applicationId:com.heiko.mm
minSdk:21
targetSdk:31
6. Gradle系列文章
Android Gradle 开发与应用 (一) : Gradle基础-氦客-CSDN博客
Android Gradle开发与应用 (二) : Groovy基础语法-氦客-CSDN博客
Android Gradle开发与应用 (三) : Groovy语法概念与闭包-氦客-CSDN博客
Android Gradle开发与应用 (四) : Gradle构建与生命周期-氦客-CSDN博客
基于Gradle 8.2,创建Gradle插件-氦客-CSDN博客
Android Gradle插件开发_实现自动复制文件插件-氦客-CSDN博客
相关文章:
Android Gradle开发与应用 (三) : Groovy语法概念与闭包
1. Groovy介绍 Groovy是一种基于Java平台的动态编程语言,与Java是完全兼容,除此之外有很多的语法糖来方便我们开发。Groovy代码能够直接运行在Java虚拟机(JVM)上,也可以被编译成Java字节码文件。 以下是Groovy的一些…...
Android 14 设置锁屏为NONE后开启双卡PIN锁,重启设备后,输完卡1的PIN码就进入了安卓界面,未提示输入卡2的PIN码
一.问题背景 目前在多个Android14平台发现开启双卡PIN码并且关闭屏幕锁的情况下,第二个PIN码锁输入弹框不能弹出问题,导致第二个卡不能注网。 如下是未修改前重启后解锁卡1PIN码的状态 可以看出卡2不能正常使用 二.何处关闭了卡2的PIN锁? 1.添加日志 首先在KeyguardSecu…...
2024 GoLand激活,分享几个GoLand激活的方案
文章目录 GoLand公司简介我这边使用GoLand的理由GoLand 最新变化GoLand 2023.3 最新变化AI Assistant 正式版GoLand 中的 AI Assistant:_Rename_(重命名)GoLand 中的 AI Assistant:_Write documentation_(编写文档&…...
linux中对信号的认识
信号的概念与相关知识认识 信号是向目标进程发送消息通知的的一种机制。 信号可以以异步的方式发送给进程,也就是说,进程无需主动等待,而是在任何时间都可以接收到信号。 信号的种类 用kill-l命令查看系统定义的信号列表: 前台…...
【万题详解】P1048 [NOIP2005 普及组] 采药
题目描述 链接——题目在这里!!! 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草…...
Golang基于Redis bitmap实现布隆过滤器(完结版)
Golang基于Redis bitmap实现布隆过滤器(完结版) 为了防止黑客恶意刷接口(请求压根不存在的数据),目前通常有以下几种做法: 限制IP(限流)Redis缓存不存在的key布隆过滤器挡在Redis前 …...
Java基础-内部类
内部类 引言内部类的共性成员内部类静态内部类非静态内部类 局部内部类匿名内部类内部类的使用场景和好处 引言 Java不仅可以定义变量和方法,还可以定义类. 内部类允许你把一些逻辑相关的类组织在一起,并可以控制内部中类的可见性. 这么看来,内部类就像是代码一种隐藏机制:将类…...
设计模式-行为型模式-职责链模式
在软件系统运行时,对象并不是孤立存在的,它们可以通过相互通信协作完成某些功能,一个对象在运行时也将影响到其他对象的运行。行为型模式(Behavioral Pattern)关注系统中对象之间的交互,研究系统在运行时对…...
代码随想录算法训练营第四十天|LeetCode343 整数拆分、LeetCode96 不同的二叉搜索树
343.整数拆分 思路:确定dp数组以及下标的含义 dp[i]代表 i可以被拆分后的最大乘积。确定递推公式,假如拆成连个数,dp[i] j*(i-j),拆成两个数以上,dp[i]j*dp[i-j],j的范围为1到i-1.dp[i]找到所有情况的最大值。初始化…...
接口自动化测试用例如何设计
说到自动化测试,或者说接口自动化测试,多数人的第一反应是该用什么工具,比如:Python Requests、Java HttpClient、Apifox、MeterSphere、自研的自动化平台等。大家似乎更关注的是哪个工具更优秀,甚至出现“ 做平台的 &…...
弱电综合布线:连接现代生活的纽带
在当今信息化快速发展的时代,弱电网络布线作为信息传输的重要基础设施,其作用日益凸显。它不仅保障了数据的高效流通,还确保了通信的稳定性。从商业大厦到教育机构,从政府机关到医院急救中心,再到我们居住的社区&#…...
Java零基础 - 数组的定义和声明
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...
CSS补充(下),弹性布局(上)
高级选择器 1.兄弟选择器 2.同时满足 div.bg{background-color: red;}p.bg{background-color: green;}spam.bg{background-color: blue;}注:选择器中间没有空格,有明确标识的选择器写在后面 3.各种伪类的应用 3.1作为第几个子元素 选择器:nth-child…...
图数据库 之 Neo4j - 应用场景4 - 反洗钱(9)
原理 Neo4j图数据库可以用于构建和分析数据之间的关系。它使用节点和关系来表示数据,并提供实时查询能力。通过使用Neo4j,可以将大量的交易数据导入图数据库,并通过查询和分析图结构来发现洗钱行为中的模式和关联。 案例分析 假设有一家转账服务公司,有以下交易数据,每个…...
uboot分区介绍
RK平台的U-Boot支持两种分区表 RK paramter格式(旧)和 标准GPT格式(新),当机器上同时存在 两种分区表时,优先使用GPT分区表。无论是 GPT 还是 RK parameter,烧写用的分区表文件都叫parameter.t…...
快速收集诊断信息,敏捷诊断工具obdiag应用实践——《OceanBase诊断系列》之三
1. 前言 作为OceanBase的敏捷诊断工具,obdiag具有以下特点: 部署便捷:提供rpm包和OBD上部署的模式,都能够一键部署安装。用户可以选择将其部署到集群中任意一台能连接到各个节点的设备上,而不仅限于OBServer节点。即…...
C++错误总结(1)
1.定义函数类型时,如果没有返回值,用void void swap(int &x, int &y){ int tem x; x y; y tem; } 2.输入时,不加换行符 cin >> a >> b >> c >> endl ;(红色标记的是错误的部分) 3.【逆序出入…...
std::shared_from_this注意事项:exception bad_weak_ptr
1.不可以在构造函数中调用shared_from_this() 因为它的实现是: _LIBCPP_INLINE_VISIBILITYshared_ptr<_Tp> shared_from_this(){return shared_ptr<_Tp>(__weak_this_);}也就是它依赖的__weak_this_此时还未创建完成。 2.一定要public继承 class MyTy…...
【工具】Raycast – Mac提效工具
引入 以前看到同事们锁屏的时候,不知按了什么键,直接调出这个框,然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还是蛮炫酷的~ 调研 但是由于之前比较繁忙,这件事其实都忘的差不多了࿰…...
蓝桥杯集训·每日一题2024 (二分,双指针)
前言: 开学了,平时学习的压力也逐渐大起来了,不过还算可以接受,等到后面阶段考的时候就不一样了,我目前为了转专业退选了很多课,这些课我都需要花时间来刷绩点,不然保研就没有竞争力了。我自己会…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...
