协程: Flow 异步流 /

以异步方式返回多个返回值的方案:

在 Kotlin 协程 Coroutine 中 , 使用 suspend 挂起函数 以异步的方式 返回单个返回值肯定可以实现 , 如果要 以异步的方式 返回多个元素的返回值 , 可以使用如下方案 :

  • 集合
  • 序列
  • Suspend 挂起函数
  • Flow 异步流

同步调用返回多个值的弊端:

同步调用函数时 , 如果函数耗时太长或者中途有休眠 , 则会阻塞主线程导致 ANR 异常 ;

协程中 调用挂起函数 返回集合

如果要 以异步方式 返回多个返回值 , 可以在协程中调用挂起函数返回集合 , 但是该方案只能一次性返回多个返回值 , 不能持续不断的 先后 返回 多个 返回值 ;

// 携程中调用挂起函数返回多个值// 调用 " 返回 List 集合的挂起函数 " , 并遍历返回值runBlocking {listFunction().forEach {// 遍历打印集合中的内容Log.e(TAG, "$it")}}

使用 Flow 异步流持续获取不同返回值

序列可以先后返回多个返回值 , 但是会阻塞线程 ;序列可以先后返回多个返回值 , 但是会阻塞线程 ;

本篇博客中开始引入 Flow 异步流的方式 , 持续性返回多个返回值 ;

调用 flow 构建器 , 可创建 Flow 异步流 , 在该异步流中, 异步地产生指定类型的元素 ;

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

在 flow 异步流构建器中 , 通过调用 FlowCollector#emit 生成一个元素 ; 函数原型如下 :

/*** [FlowCollector]用作流的中间或终端收集器,并表示接收[Flow]发出的值的实体。* 该接口通常不应该直接实现,而是在实现自定义操作符时作为[flow]构建器中的接收器使用。* 这个接口的实现不是线程安全的。*/
public interface FlowCollector<in T> {/*** 收集上游发出的值。* 此方法不是线程安全的,不应该并发调用。*/public suspend fun emit(value: T)
}

调用 Flow#collect 函数, 可以获取在异步流中产生的元素 , 并且该操作是异步操作, 不会阻塞调用线程 ;

public interface Flow<out T> {/*** 接收给定的[collector]并[发出][FlowCollector]。向它发射]值。* 永远不应该实现或直接使用此方法。** 直接实现“Flow”接口的唯一方法是扩展[AbstractFlow]。* 要将它收集到特定的收集器,可以使用' collector. emitall (flow) '或' collect{…}的扩展* 应该使用。这样的限制确保了上下文保存属性不被侵犯,并防止了大多数情况* 与并发性、不一致的流调度程序和取消相关的开发人员错误。*/@InternalCoroutinesApipublic suspend fun collect(collector: FlowCollector<T>)
}

 案例

class FlowActivity  : ComponentActivity() {override   fun onCreate(savedInstanceState: Bundle?){// 协程中调用挂起函数flowFunction()返回一个 Flow 异步流runBlocking{// 调用 Flow#collect 函数, 可以获取在Flow异步流中产生的元素值itval mFlow: Flow<Int> = flowFunction()mFlow.collect(collector = {// 每隔 500ms 即可  获取    Flow异步流中的一个Int 元素// 并且该操作是异步操作, 不会阻塞调用线程Log.e(TAG, "获取Flow异步流中的一个Int元素it=$it")})}
/*** 获取Flow异步流中的一个Int元素it=0* 获取Flow异步流中的一个Int元素it=1* 获取Flow异步流中的一个Int元素it=2* */}/*** 使用 flow 构建器 Flow 异步流* 在该异步流中, 异步地产生 Int 元素*/suspend fun flowFunction(): Flow<Int>{val flow : Flow<Int> = flow<Int>(block = {Log.e(TAG, "输出接受者对象this=${this}")for (i in 0..2) {// 挂起函数 挂起 500ms// 在协程中, 该挂起操作不会阻塞调用线程, 会继续执行其它代码指令// 500ms 恢复执行, 继续执行挂起函数之后的后续代码指令delay(500)// 每隔 500ms 产生一个元素// 通过调用 FlowCollector#emit 生成一个元素this.emit(i)}})return  flow}}

Flow 异步流获取返回值方式与其它方式对比

① 异步流构建方式 : Flow 异步流是通过 flow 构建器函数 创建的 ;

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

② 构建器可调用挂起函数 : flow 构建器代码块中的代码 , 是可以挂起的 , 可以在其中调用 挂起函数 , 如 kotlinx.coroutines.delay 函数等 ;

 /*** 使用 flow 构建器 Flow 异步流* 在该异步流中, 异步地产生 Int 元素*/suspend fun flowFunction(): Flow<Int>{val flow : Flow<Int> = flow<Int>(block = {Log.e(TAG, "输出接受者对象this=${this}")for (i in 0..2) {// 挂起函数 挂起 500ms// 在协程中, 该挂起操作不会阻塞调用线程, 会继续执行其它代码指令// 500ms 恢复执行, 继续执行挂起函数之后的后续代码指令delay(500)// 每隔 500ms 产生一个元素// 通过调用 FlowCollector#emit 生成一个元素this.emit(i)}})return  flow}

③ suspend 关键字可省略 : 返回值为 Flow 异步流的函数 , 其默认就是 suspend 挂起函数 , suspend 关键字可以省略 , 上述函数中不标注 suspend 也可 ;

 /*** 使用 flow 构建器 Flow 异步流* 在该异步流中, 异步地产生 Int 元素*///suspend fun flowFunction(): Flow<Int>{fun flowFunction(): Flow<Int>{val flow : Flow<Int> = flow<Int>(block = {Log.e(TAG, "输出接受者对象this=${this}")for (i in 0..2) {// 挂起函数 挂起 500ms// 在协程中, 该挂起操作不会阻塞调用线程, 会继续执行其它代码指令// 500ms 恢复执行, 继续执行挂起函数之后的后续代码指令delay(500)// 每隔 500ms 产生一个元素// 通过调用 FlowCollector#emit 生成一个元素this.emit(i)}})return  flow}

④ 生成元素 : 在 Flow 异步流中 , 通过调用 FlowCollector#emit 函数生成元素 ;

⑤ 收集元素 : 在 Flow 异步流中 , 通过调用 Flow#collect 函数可以收集 在 Flow 异步流中生成的元素 ;

在 Android 中 使用 Flow 异步流下载文件

Android 中主线程不可执行网络相关操作 , 因此只能在 子线程 中下载文件 ,可以在协程中使用 Dispatcher.IO 调度器在子线程下载文件 ,下载文件时需要实时显示下载百分比进度 ,这个进度需要上报给主线程 , 在主线程中更新 UI 显示下载进度 ,在 Flow 异步流中 , 可以 使FlowCollector#emit 向主线程中发送进度值 。在主线程中 , 可以 使用 Flow#collect 函数 收集 Flow 异步流中发射出来的数据 , 如 : 进度 , 捕获的异常 , 下载状态等 ;

完整流程 , 如下图所示 :

Flow 冷流 ( 流被收集时运行 )

Flow 异步流构建器函数 flow 函数 中的 代码 ,在 调用 Flow#collect 函数 时 , 也就是在 Flow 异步流 收集元素时 ,才会 执行 flow 构建器 中的代码 ;这种机制的异步流 称为 冷流 ;

Flow 异步流冷流代码示例 :

在 flow 构建器的开始位置 , 发射元素 , 在主线程中 Flow#collect 收集元素位置 , 添加日志信息 , 查看日志打印的时机 ;

class FlowActivity  : ComponentActivity() {override   fun onCreate(savedInstanceState: Bundle?){// 协程中调用挂起函数flowFunction()返回一个 Flow 异步流runBlocking{// 调用 Flow#collect 函数, 可以获取在Flow异步流中产生的元素值itval mFlow: Flow<Int> = flowFunction()Log.e(TAG, "Flow 异步流冷流 开始收集元素")mFlow.collect(collector = {// 每隔 500ms 即可  获取    Flow异步流中的一个Int 元素// 并且该操作是异步操作, 不会阻塞调用线程Log.e(TAG, "收集Flow异步流冷流中的一个个元素it=$it")})}
/**
Flow 异步流冷流 开始收集元素
Flow 异步流冷流开始发射元素Flow 异步流冷流发射元素值i=0
收集Flow异步流冷流中的一个个元素it=0Flow 异步流冷流发射元素值i=1
收集Flow异步流冷流中的一个个元素it=1Flow 异步流冷流发射元素值i=2
收集Flow异步流冷流中的一个个元素it=2
*/}/*** 使用 flow 构建器 Flow 异步流* 在该异步流中, 异步地产生 Int 元素*/suspend fun flowFunction(): Flow<Int>{val flow : Flow<Int> = flow<Int>(block = {Log.e(TAG, "输出接受者对象this=${this}")Log.e(TAG,"Flow 异步流冷流开始发射元素")for (i in 0..2) {// 挂起函数 挂起 500ms// 在协程中, 该挂起操作不会阻塞调用线程, 会继续执行其它代码指令// 500ms 恢复执行, 继续执行挂起函数之后的后续代码指令delay(500)// 每隔 500ms 产生一个元素// 通过调用 FlowCollector#emit 生成一个元素Log.e(TAG,"Flow 异步流冷流发射元素值i=$i")this.emit(i)}})return  flow}}

Flow 流的连续性

Flow 流 每次调用 Flow#collect 收集元素的操作 , 都是 按照 固定顺序 执行的 , 使用 特殊操作符 可以改变该顺序 ;

Flow 异步流 中的元素 , 按照顺序进行 FlowCollector#emit 发射元素操作 , 则 调用 Flow#collect 收集元素时获取的元素 也是按照顺序获取的 ;

在流的 上游发射元素   到  下游收集元素  的过程中 , 会 使用 过渡操作符 处理每个 FlowCollector#emit 发射出的元素 , 最终才交给 最末端的 操作符 ;

class FlowActivity  : ComponentActivity() {override   fun onCreate(savedInstanceState: Bundle?){// 协程中调用挂起函数返回一个 Flow 异步流runBlocking{// 使用下面的方式asFlow()可以快速构建一个 Flow 流//上游发射元素val mFlow : Flow<Int> = (0..5).asFlow()val filterFlow: Flow<Int> = mFlow.filter (predicate={// Log.e(TAG, "下游收集元素it=$it")// 奇数才能继续向下流,偶数被过滤掉了it % 2 == 1})filterFlow.collect(collector = {Log.e(TAG, "下游收集Flow异步流冷流filterFlow过滤后满足条件的一个个元素it=$it")})//将Flow异步流冷流: filterFlow的元素值进行转换后,返回一个新的Flow流 ,然后对流的元素进行发射val mapFlow : Flow<String> = filterFlow.map<Int,String>(transform={// 遍历元素, 将其拼接成字符串"学号 : $it"})mapFlow.collect(collector = {Log.e(TAG, "下游收集Flow异步流冷流mapFlow的一个个元素it=$it")})}/** 下游收集Flow异步流冷流filterFlow过滤后满足条件的一个个元素it=1*  下游收集Flow异步流冷流filterFlow过滤后满足条件的一个个元素it=3*  下游收集Flow异步流冷流filterFlow过滤后满足条件的一个个元素it=5*  下游收集Flow异步流冷流mapFlow的一个个元素it=学号 : 1* 下游收集Flow异步流冷流mapFlow的一个个元素it=学号 : 3* 下游收集Flow异步流冷流mapFlow的一个个元素it=学号 : 5* */}}

鸣谢:

mAndroid面试题之Kotlin异步流、冷流Flow (qq.com)

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

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

相关文章

江协科技51单片机学习- p17 定时器

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Unity面试题 UGUI调整UI与粒子特效的显示层级

首先&#xff0c;必须保证Canvas画布的渲染模式为了相机渲染 方法:一&#xff1a;将需要控制UI显示层级的Image换成Sprite 1.创建一个粒子系统&#xff0c;和两张Sprite. 2.设置Sprite1的Order in Layer为 -1&#xff0c;设置Sprite2的Order in Layer为 1&#xff0c;粒子的Ord…

LONGHEADS:无需训练的多头注意力长文本处理框架

大模型&#xff08;LLMs&#xff09;在处理海量文本数据时展现出了前所未有的能力。然而这些模型在面对超出其训练时所见序列长度的长文本时存在两个主要问题&#xff1a;一是模型对于超出预训练长度的文本难以有效泛化&#xff0c;二是注意力机制的二次方时间复杂度导致计算成…

活动预告|探索 LLM 大模型的小型化 —— 微软 Phi3在 NVIDIA Jetson 与 NIM 平台的最佳实践

在当前高速发展的人工智能领域&#xff0c;如何高效的部署和优化 SLM (小型的大模型) 成为关键。随着微软 Phi-3 系列模型的发布&#xff0c;让 SLM 在 NVIDIA Jetson 边缘计算平台上的部署成为可能。同时 Phi-3 系列模型已在 NVIDIA NIM 平台提供加速的推理服务。 NVIDIA NIM…

解决File协议导致的CORS限制,用Node.js搭建本地服务器

文章目录 一、前言二、分析报错原因三、如何解决四、具体步骤 你是否曾遇到这样的困境&#xff1a;在本地使用file://协议直接打开HTML文件时&#xff0c;由于现代浏览器的安全限制&#xff0c;无法跨源请求&#xff08;CORS&#xff09;本地资源&#xff1f;尤其是当你试图通过…

老电脑焕发第二春,玩转 Stable Diffusion 3

几年前&#xff0c;我头脑一热&#xff0c;配置了一台顶配级消费 PC&#xff08;RTX 2080 Ti GPU i9 CPU&#xff09;&#xff0c;打算用来学习 AI。然而&#xff0c;起初我并没有找到合适的切入点。深度学习早期阶段&#xff0c;消费级显卡根本无法承担训练大模型、微调大模型…

ONLYOFFICE 桌面编辑器8.1---一个高效且强大的办公软件

软件介绍 ONLYOFFICE 桌面编辑器经过不断的更新换代现在迎来了&#xff0c;功能更加强大的ONLYOFFICE 桌面编辑器8.1是一个功能强大的办公套件&#xff0c;专为多平台设计&#xff0c;包括Windows、Linux和macOS。它提供了一套全面的办公工具&#xff0c;包括文档处理、电子表…

elementplus el-table(行列互换)转置

Element Plus v2.4.0, repl v3.4.0 <template> <div><el-table :data"tableData" style"width: 100%"><el-table-column prop"name" label"名字" width"180" /><el-table-column prop"wei…

如何linux 查询进程和杀死进程

在程序开启长链接的时候&#xff0c;有时候会发现端口被占用的情况&#xff0c;但是又没有启动相关的端口&#xff0c;所以我们需要将端口占用结束掉 1.使用指令netstat -ntlp 查看当前有哪些进程&#xff0c;如图&#xff1a; 2.使用指令kill -9 18785 杀死进程&#xff0c…

Charles抓包工具系列文章(一)-- Compose 拼接http请求

一、背景 众所周知&#xff0c;Charles是一款抓包工具&#xff0c;当然是http协议&#xff0c;不支持tcp。&#xff08;如果你想要抓tcp包&#xff0c;请转而使用wireshark&#xff0c;在讲述websocket的相关技术有梳理过wireshark抓包&#xff09; 话说回来&#xff0c;char…

【自然语言处理系列】Python 字符串操作技巧:清理、替换与合并

在编写Python程序时&#xff0c;字符串处理是一项常见的任务。了解如何有效地清理、修改和合并字符串对于数据预处理、文本分析和日常编程都至关重要。本文将引导您通过一系列实用的示例来掌握Python中字符串的核心操作&#xff0c;包括去除不需要的空格和特殊字符、替换文本中…

【机器学习项目实战(二)】基于朴素贝叶斯的中文垃圾短信分类

完整代码、数据集和相应的报告 链接已经放在了正文最下方, 供大家参考学习 摘要 ​ 本文探讨了中文垃圾短信分类的问题,通过收集实际数据集,运用多种机器学习算法进行分类,并对比了不同算法在垃圾短信分类任务上的性能。本研究旨在提高中文垃圾短信的识别准确率,为构建更…

java溯本求源之基础(二十五)之--ArrayList常用方法介绍

1. 介绍 1.1简介 ArrayList ArrayList 是 Java 集合框架中的一个类&#xff0c;位于 java.util 包中。它实现了 List 接口&#xff0c;提供了一个动态数组的功能。与普通数组不同&#xff0c;ArrayList 可以在需要时自动调整其容量&#xff0c;以容纳更多的元素。这使得它非常…

鸣潮基于虚幻引擎4的多平台效果和性能优化实践

《鸣潮》基于虚幻引擎4的多平台效果和性能优化实践 | 王宏波 库洛游戏 文章目录 《鸣潮》基于虚幻引擎4的多平台效果和性能优化实践 | 王宏波 库洛游戏Why Deferred Shading移动端高质量的TAAU渲染流程Ghost和Flicker优化&#xff0c;一些图像空间算法的融入动静态像素的差异处…

第三代全智能AI无人直播系统:销售变现的革新利器

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经(keJ0277)逐渐渗透到我们生活的方方面面。特别是在直播销售领域&#xff0c;第三代全智能AI无人直播系统的出现&#xff0c;正成为销售变现的革新利器&#xff0c;引领着行业的新一轮变革。一、全智能AI无…

mysql数据库的管理

MySQL数据库管理 mysql数据文件 存放在初始化时定义的datadir 该目录下放置三种后缀文件 .frm 与表相关的元数据&#xff08;meta&#xff09;&#xff0c;表结构的定义信息等。 .MYD mylSAM存储引擎专用 .MYI mylSAM存储引擎专用 MySQL语言类型 DDL&#xff08;数据定义语言…

App推广新突破!Xinstall无注册码方案,一键解决您的获客难题

在互联网的浪潮中&#xff0c;App推广与运营面临着前所未有的挑战。随着流量红利的衰退&#xff0c;如何高效、精准地触达用户&#xff0c;提升用户留存和转化率&#xff0c;成为每个企业都必须面对的问题。在这个关键时刻&#xff0c;Xinstall以其无注册码的解决方案&#xff…

使用vant4+vue3制作电商购物网站

一、前言 1.本项目基于vant4vue3构建&#xff0c;默认友友们已具备相关知识&#xff0c;如不具备&#xff0c;请友友们先去了解相关该概念 2.项目数据来源于开源框架 新峰商城 在此指出 3.此项目目的在于帮助友友们了解基本的用法&#xff0c;没有涉及太多的逻辑操作。 二、…

针对AIGC检测的鲁棒性测试——常见攻击手段汇总

前言&#xff1a;这篇文章来总结一下针对AIGC检测的常见攻击手段&#xff0c;选取的研究工作均出自近5年AIGC检测相关文章。&#xff08;论文被拒了需要补实验&#xff0c;先来看看别人怎么做的……&#xff09; 2019 WIFS Detecting and Simulating Artifacts in GAN Fake Ima…

JavaScript的学习之事件的简介

目录 一、事件是什么 二、如何处理事件 一、事件是什么 定义&#xff1a;事件就是浏览器和用户之间的交互行为。 例如&#xff1a;点击按钮、鼠标移动、关闭窗口等。 二、如何处理事件 我们可以在对应的事件属性中设置一些JS行为&#xff0c;当事件触发的时候会将这些代码执行…