Kotlin Flow 操作符

前言

Kotlin 拥有函数式编程的能力,使用Kotlin开发,可以简化开发代码,层次清晰,利于阅读。

然而Kotlin拥有操作符很多,其中就包括了flow。Kotlin Flow 如此受欢迎大部分归功于其丰富、简洁的操作符,巧妙使用Flow操作符可以大大简化我们的程序结构,提升可读性与可维护性。本篇文章列举一些比较常用 Kotlin 操作符,通过关键原理与使用场景列子来讲解Flow操作符。

1. collect接收操作符

用于数据接收,此操作符没有返回对象,后面不可再添加操作符:


fun flowCollect() {
//流启动viewModelScope.launch {flow {for (i in 1..3) {Log.d("TAG"," flowCollect $i")emit(i)delay(1000)}}.collect {Log.d("TAG"," collect $it")}//后面不可再接收其他操作符}}

2. launchIn操作符

流的启动主要有两种,一种是上面的作用域.launch启动一个流,用collect操作符接收数据;

一种是launchIn操作符启动流,官方不建议用这种,可能出于数据安全考虑,一般建议在onResume方法调用后启动协程,因为怕数据接收了,但View还没创建出来。但链式调用真的很好用,用onEach操作符接收数据。

flow {for (i in 1..3) {Log.d("TAG"," flowCollect $i")emit(i)delay(1000)}}.onCompletion {Log.d("TAG","onCompletion ")}.launchIn(viewModelScope)//在ViewModel中,直接launchIn在ViewModelScope作用域

3. onEach操作符

返回一个流,该流在上游流的每个值向下游发出之前调用给定的操作。也可以用来接收数据,与上面collect不同的是此操作符返回流,我们后面还可接其他操作符。上面的launchIn操作符启动时,可用于接收数据。

 fun flowOnEach() {flow {for (i in 1..3) {LogUtils.d("Emitting $i")emit(i)delay(1000)}}.onEach {LogUtils.d("onEach $it")}.onCompletion {LogUtils.d("onCompletion ")}.launchIn(viewModelScope)}

4. reduce操作符

reduce 操作符可以将所有数据累加(加减乘除)得到一个结果,如下所示:
在这里插入图片描述

    fun testReduce(){val result = listOf(1, 2, 3).reduce { a, b ->a + b}print("list reduce result:$result")}

查看测试结果输出如下:
在这里插入图片描述

注意⚠️:
1: 如果 flow 中没有数据,将抛出异常。如不希望抛异常,可使用 reduceOrNull 方法。
2: reduce 操作符不能变换数据类型。比如,Int 集合的结果不能转换成 String 结果。

5. fold操作符

fold 和 reduce 很类似,但是 fold 可以变换数据类型
在这里插入图片描述

有时候,我们不需要一个结果值,而是需要继续操 flow,可使用 runningFold :

flowOf(1, 2, 3).runningFold("a") { a, b ->a + b
}.collect {println(it)
}

查看输出结果:

a
a1
a12
a123

同样的,reduce 也有类似的方法 runningReduce:

flowOf(1, 2, 3).runningReduce { a, b ->a + b
}.collect {println(it)
}

查看输出结果:

1
3
6

6. debounce操作符

debounce 需要传递一个毫秒值参数,功能是:只有达到指定时间后才发出数据,最后一个数据一定会发出。
在这里插入图片描述

例如,定义 1000 毫秒,也就是 1 秒,被观察者发出数据,1秒后,观察者收到数据,如果 1 秒内多次发出数据,则重置计算时间。

flow {emit(1)delay(500)emit(2)delay(550)emit(3)delay(1000)emit(4)delay(1010)
}.debounce(1000
).collect {println(it)
}

查看输出结果,只有休眠1000和1010符合要求:

3
4

所以,rebounce 的应用场景是限流功能。

7. sample操作符

sample 和 debounce 很像,区别是:在规定时间内,只发送一个数据:
在这里插入图片描述

flow {repeat(4) {emit(it)delay(50)}
}.sample(100).collect {println(it)
}

输出结果:

1
3

所以,sample 的应用场景是截流功能。

8. flatmapMerge操作符

简单的说就是获得两个 flow 的乘积或全排列,合并并且平铺,发出一个 flow。
在这里插入图片描述

flowOf(1, 3).flatMapMerge {flowOf("$it a", "$it b")
}.collect {println(it)
}

输出结果:

1 a
1 b
3 a
3 b

注意⚠️:flatmapMerge 还有一个特性,flatmapMerge 可以设置并发量,可以理解为 flatmapMerge 是线程安全的,而 flatmapConcat 不是线程安全的。会在下一个操作符里提及。

9. flatmapConcat操作符

举个列子:

flowOf(1, 3).flatMapConcat {flowOf("a", "b", "c")
}.collect {println(it)
}

在这里插入图片描述

功能和 flatmapMerge 一致,不同的是 flatmapMerge 可以设置并发量,可以理解为 flatmapMerge 是线程安全的,而 flatmapConcat 不是线程安全的。

本质上,在 flatmapMerge 的并发参数设置为 1 时,和 flatmapConcat 基本一致,而并发参数大于 1 时,采用 channel 的方式发出数据,具体内容请参阅源码。

10. buffer操作符

介绍 buffer 的时候,先要看这样一段代码:

flowOf("A", "B", "C", "D")
.onEach {println("1 $it")
}
.collect { println("2 $it") }

我们注意查看一下输出结果:

1 A
2 A
1 B
2 B
1 C
2 C
1 D
2 D

如果我们加上 buffer 的代码:

flowOf("A", "B", "C", "D")
.onEach {println("1 $it")
}
.buffer()
.collect { println("2 $it") }

在查看一下加上 buffer 的代码的输出结果:

1 A
1 B
1 C
1 D
2 A
2 B
2 C
2 D

对比俩次输出结果,会发现输出内容有所不同,buffer 操作符可以改变收发顺序,像有一个容器作为缓冲似的,在容器满了或结束时,下游开始接到数据,onEach 添加延迟,效果更明显。

11 combine操作符

合并两个 flow,长的一方会持续接受到短的一方的最后一个数据,直到结束:

flowOf(1, 3).combine(flowOf("a", "b", "c")
) { a, b -> b + a }
.collect {println(it)
}

查看输出结果如下:

a1
b3
c3

12. zip操作符

也是合并两个 flow,结果长度与短的 flow 一致,很像木桶原理。

    fun testZip() {runBlocking {val time = measureTimeMillis {val flow1 = flow { emit("a") emit ("b") emit ("c") emit ("d") }val flow2 = flow { emit("1") emit ("2") } flow1 . zip (flow2) { sex, subject -> "$sex-->$subject" }.collect { println(it) }} println ("use time:$time")}}

查看输出结果:

a-->1
b-->2
use time:71

通过输出可以看出flow2先结束了,并且flow1没发送完成。
zip原理简单来说:
在这里插入图片描述
可以看出,zip的特点:

短的Flow结束,另一个Flow也结束。

13. flatMapLatest操作符

与 collectLatest 操作符类似,处理最新值,也有相对应的“最新”展平模式,在发出新流后立即取消先前流的收集。 这由 flatMapLatest 操作符来实现。

14. distinctUntilChanged操作符

和前一个数据不同,才能收到,和前一个数据相同,则会被过滤掉。
在这里插入图片描述

flowOf(1, 1, 2, 2, 3, 1).distinctUntilChanged().collect {println(it)
}

运行查看输出结果:

1
2
3
1

总结

Kotlin 拥有函数式编程的能力,同时Kotlin拥有很多操作符,其中就包括了flow。合理使用Kotlin操作符可以使我们的代码更加简化,层次清晰,利于阅读。因此,要灵活使用Kotlin操作符。

参考:

  1. 协程文档
  2. 协程中文网
  3. 这一次,让Kotlin Flow 操作符真正好用起来
  4. Kotlin 协程Flow主要操作符(一)
  5. Kotlin 协程Flow主要操作符(二)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/211367.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构入门————树(C语言/零基础/小白/新手+模拟实现+例题讲解)

目录 1. 树的概念及其结构 1.1 树的概念: 1.2 树的相关概念: 1.3 树的表示方法: ​编辑 1.4 树的应用: 2. 二叉树的概念及其结构 2.1 概念: 2.2 特点: 2.3 特殊二叉树: 2.4 二叉树的性质&#xf…

【深度学习】注意力机制(一)

本文介绍一些注意力机制的实现,包括SE/ECA/GE/A2-Net/GC/CBAM。 目录 一、SE(Squeeze-and-Excitation) 二、ECA(Efficient Channel Attention) 三、GE(Gather-Excite) 四、A2-Net(Double A…

二维码智慧门牌管理系统升级解决方案:数字鉴权

文章目录 前言一、数字鉴权的核心机制二、数字鉴权的意义和应用 前言 随着科技的飞速发展,我们的生活逐渐进入数字化时代。在这个数字化的过程中,数据的安全性和门牌信息的保障变得至关重要。今天,我们要介绍的是二维码智慧门牌管理系统升级…

【论文复现】zoedepth踩坑

注意模型IO: 保证输入、输出精度、类型与复现目标一致。 模型推理的代码 from torchvision import transforms def image_to_tensor(img_path, unsqueezeTrue):rgb transforms.ToTensor()(Image.open(img_path))if unsqueeze:rgb rgb.unsqueeze(0)return rgbdef…

机器学习笔记 - 基于C# + .net framework 4.8的ONNX Runtime进行分类推理

该示例是从官方抄的,演示了如何使用 Onnx Runtime C# API 运行预训练的 ResNet50 v2 ONNX 模型。 我这里的环境基于.net framework 4.8的一个winform项目,主要依赖下面版本的相关库。 Microsoft.Bcl.Numerics.8.0.0 Microsoft.ML.OnnxRuntime.Gpu.1.16.3 SixLabors.ImageShar…

MyString:string类的模拟实现 1

MyString:string类的模拟实现 前言: 为了区分标准库中的string,避免编译冲突,使用命名空间 MyString。 namespace MyString {class string{private:char* _str;size_t _size;size_t _capacity;const static size_t npos -1;// C标…

2023年 - 我的程序员之旅和成长故事

2023年 - 我的程序员之旅和成长故事 🔥 1.前言 大家好,我是Leo哥🫣🫣🫣,今天咱们不聊技术,聊聊我自己,聊聊我从2023年年初到现在的一些经历和故事,我也很愿意我的故事分…

Android 样式小结

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、使用3.1 创建并应用样式3.2 创建并…

【精彩回顾】恒拓高科亮相第十一届深圳军博会

2023年12月6日-8日,由中国和平利用军工技术协会、全国工商联科技装备业商会、深圳市国防科技工业协会等单位主办以及政府相关部门支持,深圳企发展览有限公司承的“2023第11届中国(深圳)军民两用科技装备博览会(深圳军博…

02 CSS基础入门

文章目录 一、CSS介绍1. 简介2. 相关网站3. HTML引入方式 二、选择器1. 标签选择器2. 类选择器3. ID选择器4. 群组选择器 四、样式1. 字体样式2. 文本样式3. 边框样式4. 表格样式 五、模型和布局1. 盒子模型2. 网页布局 一、CSS介绍 1. 简介 CSS主要用于控制网页的外观&#…

C#如何使用SqlSugar操作MySQL/SQL Server数据库

一. SqlSugar 连接MySQL数据库 public class MySqlCNHelper : Singleton<MySqlCNHelper>{public static SqlSugarClient CnDB;public void InitDB() {//--------------------MySQL--------------------CnDB new SqlSugarClient(new ConnectionConfig(){ConnectionString…

玩转大数据12:大数据安全与隐私保护策略

1. 引言 大数据的快速发展&#xff0c;为各行各业带来了巨大的变革&#xff0c;也带来了新的安全和隐私挑战。大数据系统通常处理大量敏感数据&#xff0c;包括个人身份信息、财务信息、健康信息等。如果这些数据被泄露或滥用&#xff0c;可能会对个人、企业和社会造成严重的损…

大数据Doris(三十五):Unique模型(唯一主键)介绍

文章目录 Unique模型(唯一主键)介绍 一、创建doris表 二、插入数据

【华为OD题库-076】执行时长/GPU算力-Java

题目 为了充分发挥GPU算力&#xff0c;需要尽可能多的将任务交给GPU执行&#xff0c;现在有一个任务数组&#xff0c;数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务&#xff0c;一次执行耗时1秒&#xff0c;在保证GPU不空闲情况下&…

linux 应用开发笔记---【标准I/O库/文件属性及目录】

一&#xff0c;什么是标准I/O库 标准c库当中用于文件I/O操作相关的一套库函数&#xff0c;实用标准I/O需要包含头文件 二&#xff0c;文件I/O和标准I/O之间的区别 1.标准I/O是库函数&#xff0c;而文件I/O是系统调用 2.标准I/O是对文件I/O的封装 3.标准I/O相对于文件I/O具有更…

SpringBoot 项目 Jar 包加密,防止反编译

1场景 最近项目要求部署到其他公司的服务器上&#xff0c;但是又不想将源码泄露出去。要求对正式环境的启动包进行安全性处理&#xff0c;防止客户直接通过反编译工具将代码反编译出来。 2方案 第一种方案使用代码混淆 采用proguard-maven-plugin插件 在单模块中此方案还算简…

调用别人提供的接口无法通过try catch捕获异常(C#),见鬼了

前几天做CA签名这个需求时发现一个很诡异的事情&#xff0c;CA签名调用的接口是由另外一个开发部门的同事(比较难沟通的那种人)封装并提供到我们这边的。我们这边只需要把数据准备好&#xff0c;然后调他封装的接口即可完成签名操作。但在测试过程中&#xff0c;发现他提供的接…

[后端卷前端2]

绑定class 为什么需要样式绑定呢? 因为有些样式我们希望能够动态展示 看下面的例子: <template><div><p :class"{active:modifyFlag}">class样式绑定</p></div> </template><script>export default {name: "goo…

人力资源服务展示网站作用有哪些

就业劳务问题往往是不少人群关注的问题&#xff0c;每个城市都聚集着大量求业者&#xff0c;而人力资源管理公司每年也会新增不少&#xff0c;对求业者来说&#xff0c;通过人力资源公司可以快速便捷的找到所需工作&#xff0c;而对公司来说&#xff0c;市场大量用户可以带来收…

C语言第十八集(动态内存管理)

1.malloc函数可以开辟一块空间,具体搜: 2.malloc函数申请的空间在内存的堆区 而且它只负责帮你申请空间,不负责帮你清理空间 3.free函数可以释放内存 4.free函数释放的是内存中的堆区,具体搜: 5.在free函数调用完后记得把对应的指针设为空指针 6.calloc函数跟malloc函数差…