如何在android开发中使用Kotlin Flow(一)

news2024/11/17 7:18:41

Kotlin 的Flow可以对数据流进行建模,类似LiveData、RxJava的数据流。

Flow也是用观察者模式实现的。

观察者模式包括了可观察对象(Observable,生产者、发射者、源这些称呼都是指可观察对象,可以被观察)、观察对象(Observers,订阅者、收集者、接收者这些称呼都是指观察对象,可以观察Observable)。当有什么状态(数据)变化时,Observable会自动通知Observers。

Observable(可观察者)可以是hot(热)或者是cold(冷)的。

  • Hot Observables 可以在没有Observers(观察者)观察它们时,照常发送(发射)数据。
  • Cold Observables只可以在有Observers(观察者)观察它们时,才发送(发射)数据。

而Flow是cold(冷)的,而它发送数据的时机是当有Terminal operator(末端操作符)应用到这个Flow时。

创建Flow

  • flow操作符
fun fibonacci(): Flow<BigInteger> = flow {
        var x = BigInteger.ZERO
        var y = BigInteger.ONE
        while (true) {
            emit(x)
            x = y.also {
                y += x
            }
        }
    }

上面的代码是不会使用flow发射数据的,因为flow是冷流,需要加上末端操作符才能使flow发射数据,如collect是最常见的末端操作符:

fibonacci().take(100).collect { println(it) }

flow操作符的源码是:

public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

// Named anonymous object
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
    override suspend fun collectSafely(collector: FlowCollector<T>) {
        collector.block()
    }
}

由此可知flow操作符返回的是SafeFlow,意安全的流。因为flow支持coroutine,所以在flow操作符中不能使用其他coroutine context,在编译时,它会做这个检查。下面这个例子就是在flow内使用了不同的协程上下文,导致报错:
Caused by: java.lang.IllegalStateException: Flow invariant is violated:

        flow {
            emit(1) // Ok
            withContext(IO) {
                emit(2) // 报错
            }
        }.collect {
            println(it)
        }

如果想在改变coroutine context,可以使用flowOn操作符:

withContext(Dispatchers.Main) {
    val singleValue = flow {
                this.emit(1)
            } // 如果之间没有指定它的上下文那么就用下面指定的IO上下文
        .map { ... } // 在 IO中执行
        .flowOn(Dispatchers.IO) // 它指定的上下文只会作用在前面没有指定上下文的操作符上,对后面的操作符没有影响
        .filter { ... } // 在Default上下文中运行,因为下面的flowOn指定了
        .flowOn(Dispatchers.Default)
        .single() // 在 Main上下文中执行
}

这个coroutine context是flow执行的上下文。flowOn实际上是这样实现的,所以它只是把flow里lamda部分的代码放到一个新的context里去执行,方式如下:
请添加图片描述

  • flowOf操作符
    这个操作符其实只是在flow操作符基础上帮我们做了数据发射的工作:

请添加图片描述请添加图片描述

请添加图片描述
但是跟flow操作符不同的是,flowOf操作符不会检查执行的上下文,flow则会检查。

例子:

flowOf(1, 2, 3).collect{
            println(it)
}
  • asFlow操作符,与flowOf是一样的,只是对列表操作方便一些而已:

请添加图片描述

listOf(1, 2, 3, 5).asFlow().collect{
            println(it)
}

flow是冷的

所以我们要让它流动起来,就要使用一个末端操作符,最常见的就是collect,应用到这个flow上。 上面的例子我们已有展示。除了collect还有一些其他末端操作符。

toCollection()

这个操作符接受一个MutableCollection实例,Kotlin官方提供了两个toList和toSet。所以参考它们的使用,不难得出toCollection()的使用,即flow发射出来的数据都收集起来,然后一起返回。
请添加图片描述
例子:

       val myList = flow{
          emit(1)
          emit(2)
          emit(5)
        }.toList()
        
        println(myList)

first()和last()

它们分别是取第一个和取后一个。例子:

val firstItem = flow{
          emit(1)
          emit(2)
          emit(5)
        }.first()
val lastItem = flow{
          emit(1)
          emit(2)
          emit(5)
        }.last()

single()

这个操作符,有点特别,它只希望flow只发射一个数据,发多一个就报错。

val singleValue = flow {
                this.emit(1)
            } 
                .map {
                    it + 2
                } 
                .flowOn(IO)
                .filter { it > 0 } 
                .flowOn(Default)
                .single()
        println(singleValue)

reduce() 和 fold()

这两个操作符都是将flow处理成一个值。

val singleValue = flow {
                this.emit(1)
                this.emit(2)
                this.emit(3)
            } 
                .reduce { accumulator, value ->
                    accumulator + value
                }

        println(singleValue) // 6

fold()有一个初始值,而reduce()则没有:

val singleValue = flow {
                this.emit(1)
                this.emit(2)
                this.emit(3)
            }
                .fold(4) { acc: Int, value: Int ->
                    acc + value
                }

        println(singleValue) // 10

还记得我们说过,flow是在协程的基础上的技术,所以这些操作符都是是suspend函数,是可以被暂时挂起的,必须在协程中调用这些操作符。

launchIn()

这个末端操作符通常与onEach, onCompletion, catch操作符一起使用。
请添加图片描述
使用这个操作符的效果与下面的代码是等价的:

scope.launch { flow.collect() }

这个操作符会返回一个Job实例,意味着我们可以使用这个job来取消当前flow的数据采集工作,避免它工作太长时间,把当前的coroutine scope挂起了。

例子:

val job = flow {
	this.emit(1)
    this.emit(2)
    kotlinx.coroutines.delay(3000)
    this.emit(3)
}
.onEach { value -> println("$value ***") }
.onCompletion { cause -> println(cause) }
.catch { cause -> println("Exception: $cause") }
.launchIn(this)
        
job.cancel()

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

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

相关文章

如何安装谷歌服务框架?(Google三件套下载教程)

谷歌服务框架&#xff0c;想必大家已经了解过了&#xff0c;都知道是个什么东西。这里我在赘述一下&#xff0c;谷歌服务框架是支持谷歌应用商店&#xff08;Google Play&#xff09;正常运行的服务框架&#xff0c;没有它&#xff0c;谷歌商店无法正常在安卓手机上运行&#x…

计算机图形学 第6章 三维变换与投影

目录 # 学习要求 前置知识 三维几何变换总的式子&#xff1a; 平移变换 比例变换 旋转变换&#xff1a;绕x轴旋转 反射变换 错切变换 三维复合变换 坐标系变换 正交投影矩阵 三视图 斜投影定义 透视投影 透视变换坐标系 ## 代码 透视投影分类 # 学习要求 …

怎么把图片做成gif动图?三步搞定gif在线制作

在日常办公、生活中经常会使用一些gif动态图片&#xff0c;生动有趣画面丰富。很好奇这些gif动图是怎么制作的吧&#xff01;其实&#xff0c;制作gif表情包的方法很简单&#xff0c;给大家分享几个简单实用的gif制作&#xff08;https://www.gif.cn/&#xff09;方法&#xff…

ccflow 代码——流程讲义

爬虫组件分析目录概述需求&#xff1a;设计思路实现思路分析1.表单引擎模版表系统框架Jflow 对使用者的要求参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,…

CV——day71 零基础学YOLO:YOLOv2

YOLOv25. YOLOv25.1 YOLOv2升级概述5.2 YOLOv2网络架构5.3 基于聚类提取先验框5.4 偏移量计算方法5.5 坐标映射与还原5.6 感受野的作用5.7 特征融合改进5.7.1 多尺度检测5.8 YOLOv2 总结5. YOLOv2 5.1 YOLOv2升级概述 可以看出&#xff0c;v2的map相比v1改进了很多&#xff0c…

KafkaProducer线程安全吗?

这是我今天面试被问的一个问题&#xff0c;这会回来就来看看源码 先看KafkaProducer的构造函数 public KafkaProducer(Map<String, Object> configs) { this((ProducerConfig)(new ProducerConfig(configs)), (Serializer)null, (Serializer)null); } public KafkaPro…

[MRCTF2020]PixelShooter1题解

Android一词最早出现于法国作家利尔亚当&#xff08;Auguste Villiers de lIsle-Adam&#xff09;在1886年发表的科幻小数《未来的夏娃》&#xff08;Lve future&#xff09;中。他将外表像人的机器起名为Android。 本题是一个安卓逆向 1.下载到手机玩一玩 是一个32位文件下载…

2-JVM、JRE、JDK的作用及联系和区别

在Java环境配置和项目启动中&#xff0c;这三者的配置是项目启动的基础保证。 JVM(Java Virtual Machine)&#xff0c;Java虚拟机&#xff0c;就是一个虚拟的用于解析bytecode字节码的”虚拟计算机”。一般与OS操作系统打交道。是整个java实现跨平台的最核心的部分【使用Java编…

GlobalPointer:用统一的方式处理嵌套和非嵌套NER

本文将介绍一个称为GlobalPointer的设计&#xff0c;它利用全局归一化的思路来进行命名实体识别&#xff08;NER&#xff09;&#xff0c;可以无差别地识别嵌套实体和非嵌套实体&#xff0c;在非嵌套&#xff08;Flat NER&#xff09;的情形下它能取得媲美CRF的效果&#xff0c…

大数据专业前景怎么样?

大数据专业毕业生未来的岗位选择空间比较大&#xff0c;有三大类岗位可选择分别是大数据开发岗位、大数据分析岗位和大数据运维岗位&#xff0c;在不同的行业和技术体系结构下这些岗位也包含很多细分的岗位。 大数据开发岗位分为平台研发岗位和行业场景开发岗位两大类&#xf…

latex自己记录需要的

参考:LaTeX 公式篇 推荐网站&#xff1a;LaTeX公式编辑器&#xff0c;有模板 换行 $$ \begin{array}{c} 1\\ 2\\ 3\\ \end{array} $$123\begin{array}{c} 1\\ 2\\ 3\\ \end{array} 123​ 公式拆分 $$ \begin{split} 123 & 33 \\ &6 \end{split} $$123336\begin{split}…

找工作不用愁,送你一份Salesforce面试秘籍!

在之前的文章中&#xff0c;自由侠部落为学习者梳理了Salesforce架构师的面试秘籍&#xff0c;将其拆解为了3个方面——分享工作经验、展示技术知识以及证明领导能力&#xff0c;上篇文章已经展示了工作经验部分&#xff0c;接下来将继续分享面试秘籍&#xff0c;帮助求职者顺利…

linux内核之netlink通信

Linux内核(04)之netlink通信 Author&#xff1a;Onceday Date&#xff1a;2023年1月3日 漫漫长路&#xff0c;才刚刚开始… 参考文档&#xff1a; netlink 机制 binarydady 阿里云开发者社区linux中通用Netlink详解及使用剖析 binarydady 阿里云开发者社区RFC 3549 Linux N…

测试员最不愿遇到的18个测试问题,怎么解决?

测试员最不愿遇到的18个测试问题&#xff0c;怎么解决&#xff1f; 目录&#xff1a;导读 测试员最不愿遇到的18个测试问题&#xff0c;怎么解决&#xff1f; 一 测试充分度&#xff08;Test Sufficiency&#xff09; 二 测试有效性&#xff08;Test Effectiveness&#x…

人大金仓数据库-表的定义

表的定义 使用子查询来创建表 通过复制student表创建student_m表&#xff0c;只复制原表中的部分数据到新表 通过复制course表创建course01表&#xff0c;复制原表中的全部数据到新表 使用LIKE语法来创建表 非空约束会默认复制到新表中 create table t03(LIKE t02 INCLUDING…

设计循环队列

前言&#xff1a;队列中有一种特殊的存在——环形队列&#xff0c;其有一定的价值与意义&#xff0c;这篇文章主要由一道与其相关的例题来引出相关的知识内容。 注&#xff1a;下述解题过程是用C语言实现。 目录 一&#xff1a;题目简述 二&#xff1a;环形队列的简单介绍 …

什么是Docker?看这一篇干货文章就够了!

什么是Docker容器技术的起源容器技术 vs 虚拟机什么是容器什么是docker如何使用dockerdocker是如何工作的docker的底层实现总结作为程序员我们应怎样理解docker&#xff1f; 容器技术的起源 假设你们公司正在秘密研发下一个“今日头条”APP&#xff0c;我们姑且称为明日头条&…

ORB-SLAM3算法和代码学习——重定位Relocalization

0总述 重定位是ORB-SLAM系列保持跟踪稳定性的保障&#xff0c;从ORB-SLAM沿用至ORB-SLAM3。主要作用是在跟踪失败时&#xff0c;通过词袋向量搜索在关键帧数据库中寻找和当前帧相似的关键帧作为匹配帧&#xff0c;建立数据关联并计算当前帧的位姿&#xff0c;恢复相机的运动。…

正大国际期货:外盘短线交易九大生存准则:从亏损预期出发

一、生存是第一位 这并不是陈词滥调&#xff0c;投机是非常危险的活动。投机非并输赢那样简单&#xff0c;首要的任务是在顶峰和谷底之间的波动中生存&#xff0c;如果连生存都做不到&#xff0c;你根本就没有谈及赢的资格。 即使有了好的资金管理、正确的系统和行动的前提&a…

Ubuntu18.04下安装配置AndroidStudio软件图文教程

运行环境&#xff1a;操作系统为Ubuntu18.04&#xff0c;android-studio版本为2022.1.1.19-linux&#xff0c;Java版本为jdk8,安装路径/opt/android-studio/,当前用户为xqf222,sdk下载路径默认为/home/xqf222/Android/Sdk 详细步骤和指令如下&#xff1a; 1.安装JDK8&#xf…