前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Kotlin 协程】Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )

【Kotlin 协程】Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )

作者头像
韩曙亮
发布2023-03-30 18:28:53
1.5K0
发布2023-03-30 18:28:53
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

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


在上一篇博客 【Kotlin 协程】Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 ) 中 分析了 以异步方式 分别使用 序列 和 集合 返回多个返回值 ,

  • 序列可以先后返回多个返回值 , 但是会阻塞线程 ;
  • 集合可以一次性返回多个返回值 , 无法持续返回返回值 ;

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

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

代码语言:javascript
复制
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

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

代码语言:javascript
复制
/**
 * [FlowCollector]用作流的中间或终端收集器,并表示接收[Flow]发出的值的实体。
 * 该接口通常不应该直接实现,而是在实现自定义操作符时作为[flow]构建器中的接收器使用。
 * 这个接口的实现不是线程安全的。
 */
public interface FlowCollector<in T> {

    /**
     * 收集上游发出的值。
     * 此方法不是线程安全的,不应该并发调用。
     */
    public suspend fun emit(value: T)
}

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

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

代码示例 :

代码语言:javascript
复制
package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 携程中调用挂起函数返回一个 Flow 异步流
        runBlocking {
            // 调用 Flow#collect 函数, 可以获取在异步流中产生的元素
            flowFunction().collect {
                // 每隔 500ms 即可拿到一个 Int 元素
                // 并且该操作是异步操作, 不会阻塞调用线程
                println(it)
            }
        }
    }

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

执行结果 : 调用 Flow 异步流 , 可以持续地获取多个返回值 ; 3 个返回值间隔 500 ms 返回给了调用者 ;

代码语言:javascript
复制
2022-12-22 14:55:57.883 9026-9026/kim.hsl.coroutine I/System.out: 0
2022-12-22 14:55:58.423 9026-9026/kim.hsl.coroutine I/System.out: 1
2022-12-22 14:55:58.961 9026-9026/kim.hsl.coroutine I/System.out: 2

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


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

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

代码语言:javascript
复制
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

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

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

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

代码语言:javascript
复制
    /**
     * 使用 flow 构建器 Flow 异步流
     * 在该异步流中, 异步地产生 Int 元素
     */
    fun flowFunction() = flow<Int> {
        for (i in 0..2) {
            // 挂起函数 挂起 500ms
            // 在协程中, 该挂起操作不会阻塞调用线程, 会继续执行其它代码指令
            // 500ms 恢复执行, 继续执行挂起函数之后的后续代码指令
            delay(500)
            // 每隔 500ms 产生一个元素
            // 通过调用 FlowCollector#emit 生成一个元素
            emit(i)
        }
    }

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

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

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


Android 中主线程不可执行网络相关操作 , 因此只能在 子线程 中下载文件 ,

可以在协程中使用 Dispatcher.IO 调度器在子线程下载文件 ,

下载文件时需要实时显示下载百分比进度 ,

这个进度需要上报给主线程 , 在主线程中更新 UI 显示下载进度 ,

在 Flow 异步流中 , 可以 使用 FlowCollector#emit 向主线程中发送进度值 ,

在主线程中 , 可以 使用 Flow#collect 函数 收集 Flow 异步流中发射出来的数据 , 如 : 进度 , 捕获的异常 , 下载状态等 ;

完整流程 , 如下图所示 :

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、使用 Flow 异步流持续获取不同返回值
  • 二、Flow 异步流获取返回值方式与其它方式对比
  • 三、在 Android 中 使用 Flow 异步流下载文件
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档