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操作符。
参考:
- 协程文档
- 协程中文网
- 这一次,让Kotlin Flow 操作符真正好用起来
- Kotlin 协程Flow主要操作符(一)
- Kotlin 协程Flow主要操作符(二)
相关文章:

Kotlin Flow 操作符
前言 Kotlin 拥有函数式编程的能力,使用Kotlin开发,可以简化开发代码,层次清晰,利于阅读。 然而Kotlin拥有操作符很多,其中就包括了flow。Kotlin Flow 如此受欢迎大部分归功于其丰富、简洁的操作符,巧妙使…...

HarmonyOS4.0从零开始的开发教程08构建列表页面
HarmonyOS(六)构建列表页面 List组件和Grid组件的使用 简介 在我们常用的手机应用中,经常会见到一些数据列表,如设置页面、通讯录、商品列表等。下图中两个页面都包含列表,“首页”页面中包含两个网格布局ÿ…...

分布式环境下的session 共享-基于spring-session组件和Redis实现
1、问题概述 不是所有的项目都是单机模式的,当一个项目服务的局域比较广,用户体量比较大,数据量较大的时候,我们都会将项目部署到多台服务器上,这些个服务器都是分布在不同的区域,这样实现了项目的负载和并…...

docker基本管理和相关概念
docker是什么? docker是开源的应用容器引擎。基于go语言开发的。运行在Linux系统当中开源轻量级的“虚拟机”。 docker的容器技术可以在一台主机上轻松的为任何应用创建一个轻量级的,可移植的,自给自足的容器。 docker的宿主机是Linux系统…...

Linix服务器添加dns解析
Linix开通互联网域名地址出现,如下错误: 需要访问的服务器上添加dns解析 vim /etc/sysconfig/network-scripts/ifcfg-ens192 添加如下配置: DNS1202.96.134.13 重启网卡: systemctl restart network 注意如果是docker服务部署…...

llama.cpp部署(windows)
一、下载源码和模型 下载源码和模型 # 下载源码 git clone https://github.com/ggerganov/llama.cpp.git# 下载llama-7b模型 git clone https://www.modelscope.cn/skyline2006/llama-7b.git查看cmake版本: D:\pyworkspace\llama_cpp\llama.cpp\build>cmake --…...

STM32CubeMX+micro_ros_stm32cubemx_utils库
GitHub - micro-ROS/micro_ros_stm32cubemx_utils at humble 这个就是下载这个代码库以后的文件结构。其中sample_project.ioc就是平时STM32CubeMX的工程文件。类似于visual studio里面的项目文件 。打开以后是这个样子的: 可以看到跟本文后面的那些配置是几乎一模一…...

C语言有哪些预处理操作?
C语言的预处理是在编译之前对源代码进行处理的阶段,它主要由预处理器完成。预处理器是一个独立的程序,它负责对源代码进行一些文本替换和处理,生成经过预处理的代码。以下是C语言预处理的一些重要特性: 1,头文件包含 #…...

数据结构算法-希尔排序算法
引言 在一个普通的下午,小明和小森决定一起玩“谁是老板”的扑克牌游戏。这次他们玩的可不仅仅是娱乐,更是要用扑克牌来决定谁是真正的“大老板”。 然而,小明的牌就像刚从乱麻中取出来的那样,毫无头绪。小森的牌也像是被小丑掷…...

php使用vue.js实现省市区三级联动
参考gpt 有问题问gpt 实现效果 现省市区三级联动的方法可以使用PHP结合AJAX异步请求来实现。下面是一个简单的示例代码: HTML部分: <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>省市区三级联动…...

软件测试:测试用例八大要素模板
一、通用测试用例八要素 1、用例编号; 2、测试项目; 3、测试标题; 4、重要级别; 5、预置条件; 6、测试输入; 7、操作步骤; 8、预期输出 二、具体分析通用测试用例八要素 1、用例编号 一般是数字…...

C语言进阶之路之顶峰相见篇
目录 一、学习目标 二、宏定义 预处理 宏的概念 带参宏 无值宏定义 三、条件编译 条件编译 条件编译的使用场景 四、头文件 头文件的作用 头文件的内容 头文件的基础语句: GCC编译器的4个编译步骤: 总结 一、学习目标 掌握宏定义含义和用…...

第76讲:MySQL数据库中常用的命令行工具的基本使用
文章目录 1.mysql客户端命令工具2.mysqladmin管理数据库的客户端工具3.mysqlbinlog查看数据库中的二进制日志4.mysqlshow统计数据库中的信息5.mysqldump数据库备份工具6.mysqllimport还原备份的数据7.source命令还原SQL类型的备份文件 MySQL数据库提供了很多的命令行工具&#…...

初级数据结构(二)——链表
文中代码源文件已上传:数据结构源码 <-上一篇 初级数据结构(一)——顺序表 | NULL 下一篇-> 1、链表特征 与顺序表数据连续存放不同,链表中每个数据是分开存放的,而且存放的位置尤其零散&#…...

Kubernetes架构及核心部件
文章目录 1、Kubernetes集群概述1.1、概述1.2、通过声明式API即可 2、Kubernetes 集群架构2.1、Master 组件2.1.1、API Server2.1.2、集群状态存储2.1.3、控制器管理器2.1.4、调度器 2.2、Worker Node 组件2.2.1、kubelet2.2.2、容器运行时环境2.2.3、kube-proxy 2.3、图解架构…...

RAW和YUV的区别
RAW是指未经过任何压缩或处理的原始图像数据。在摄像头中,原始图像数据可以是来自图像传感器的未经处理的像素值。这些原始数据通常以一种Bayer模式的形式存在,其中每个像素仅包含一种颜色信息(红色、绿色或蓝色),需要…...

Linux常见问题-获取日志方法总结(Ubuntu/Debian)
1 日志基本路径和基础查看方法 在 Ubuntu 或 Debian 11 系统中,可以通过不同的日志文件来获取系统日志和内核日志。日志常见路径如下: /var/log/syslog:包含系统的整体日志,包括各种系统事件和服务日志。/var/log/auth.log&…...

【机器视觉技术栈】03 - 镜头
镜头 定焦镜头变焦镜头远心镜头 FA镜头与远心镜头的区别? 焦距越小畸变程度越大,精度要求不高的场景可以使用焦距大的FA镜头做尺寸测量,但焦距越大带来的问题就是整个机械设备越大。精度高的场景使用远心镜头进行尺寸测量。 光学基础知识…...

判断一个Series序列的值是否为单调递减Series.is_monotonic_decreasing
【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 判断一个Series序列中 各值是否单调递减 s.is_monotonic_decreasing [太阳]选择题 以下代码的输出结果中正确的是? import pandas as pd s1 pd.Series([3,2,1]) s2 pd.Series([3,2,4]) pri…...

CSPNet: A New Backbone that can Enhance Learning Capability of CNN(2019)
文章目录 -Abstract1 Introduction2 Related workformer work 3 Method3.1 Cross Stage Partial Network3.2 Exact Fusion Model 4 Experiments5 Conclusion 原文链接 源代码 - 梯度信息重用(有别于冗余的梯度信息)可以减少计算量和内存占用提高效率&am…...

本科毕业论文查重的依据
大家好,今天来聊聊本科毕业论文查重的依据,希望能给大家提供一点参考。 以下是针对论文重复率高的情况,提供一些修改建议和技巧: 本科毕业论文查重依据:维护学术诚信的基石 摘要: 本科毕业论文是衡量学生学…...

如何利用Axure制作移动端产品原型
Axure是一款专业的快速原型设计工具,作为专业的原型设计工具,Axure 能够快速、高效地创建原型,同时支持多人协作设计和版本控制管理。它已经得到了许多大公司的采用,如IBM、微软、思科、eBay等,这些公司都利用Axure 进…...

Java中时间之间的转换
Java中常见的时间类有:Date、Calendar、SimpleDateFormat等。下面对不同时间类之间的转换进行介绍。 1、Date和Calendar之间的转换 Date和Calendar都可以表示时间,但是它们的使用方式不同。Date是一个表示特定时间点的类,而Calendar则是一个…...

【win32_005】调试信息打印到控制台----2种简单方法
方法1:使用win32 api函数 PCTSTR str1 TEXT("123456789");AllocConsole();HANDLE HConsole GetStdHandle(STD_OUTPUT_HANDLE);WriteConsole(HConsole, str1, 9, NULL, NULL);https://learn.microsoft.com/zh-cn/windows/console/writeconsole 方…...

PPT添加备注
0 Preface/Foreward 1 添加备注方法 添加备注方法:在page的最下端,有一个空白文本框,该文本框用来添加备注。...

Ubuntu20.04使用cephadm部署ceph集群
文章目录 Requirements环境安装Cephadm部署Ceph单机集群引导(bootstrap)建立新集群 管理OSD列出可用的OSD设备部署OSD删除OSD 管理主机列出主机信息添加主机到集群从集群中删除主机 部署Ceph集群 Cephadm通过在单个主机上创建一个Ceph单机集群࿰…...

激光打标机在智能手表上的应用:科技与时尚的完美结合
随着科技的飞速发展,智能手表已经成为我们日常生活中不可或缺的智能设备。而在智能手表制造中,激光打标机扮演着至关重要的角色。本文将详细介绍激光打标机在智能手表制造中的应用,以及其带来的优势和影响。 一、激光打标机在智能手表制…...

ROS-ROS通信机制-参数服务器
文章目录 一、基础理论知识二、C实现三、Python实现 一、基础理论知识 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点…...

在github中通过action自动化部署 hugo academic theme,实现上传md文件更新博客内容
在github中通过action自动化部署 hugo academic theme 一、GitHub Action自动化部署Hugo博客方法 主要参考:【Hugo网站搭建】GitHub Action自动化部署Hugo博客 次要参考:使用 Github Action 自动部署 Hugo 博客 二、部署过程中遇到的问题和解决办法 …...

深入理解asyncio:异步编程的基础用法
引言: 随着计算机硬件的不断发展,对于异步编程的需求也越来越强烈。Python中的asyncio模块为开发者提供了一种强大而灵活的异步编程方式。本文将介绍asyncio的基础用法,包括async/await/run语句的使用、多个协程的并发执行、以及在协程中进行…...