当Kotlin Flow与Channel相逢

Flow之所以用起来香,Flow便捷的操作符功不可没,而想要熟练使用更复杂的操作符,那么需要厘清Flow和Channel的关系。
本篇文章构成:
在这里插入图片描述

1. Flow与Channel 对比

1.1 Flow核心原理与使用场景

原理

先看最简单的Demo:

    fun test0() {
   
        runBlocking {
   
            //构造flow
            val flow = flow {
   
                //下游
                emit("hello world ${
     Thread.currentThread()}")
            }
            //收集flow
            flow.collect {
   
                //下游
                println("collect:$it ${
     Thread.currentThread()}")
            }
        }
    }

打印结果:

collect:hello world Thread[main,5,main] Thread[main,5,main]

说明下游和上游运行在同一线程里。
在这里插入图片描述
一个最基本的flow包含如下几个元素:

  1. 操作符,也即是函数
  2. 上游,通过构造操作符创建
  3. 下游,通过末端操作符构建

我们可以类比流在管道里流动

上游早就准备好了,只是下游没有发出指令,此时上下游是没有建立起关联的,只有当下游渴了,需要水了才会通知上游放水,这个时候上下游才关联起来,管道就建好了。
因此我们认为Flow是冷流。

更多kotlin细节请移步:Kotlin Flow啊,你将流向何方?

使用
基于Flow的特性,通常将其用在提供数据的场景,比如生产数据的模块将生产过程封装到flow的上游里,最终创建了flow对象。
而使用数据的模块就可以通过该flow对象去收集上游的数据,如下:

//提供数据的模块
class StudentInfo {
   
    fun getInfoFlow() : Flow<String> {
   
        return flow {
   
            //假装构造数据
            Thread.sleep(2000)
            emit("name=fish age=18")
        }
    }
}

//消费数据的模块
    fun test1() {
   
        runBlocking {
   
            val flow = StudentInfo().getInfoFlow()
            flow.collect {
   
                println("studentInfo:$it")
            }
        }
    }

1.2 Channel核心原理与使用场景

原理
由上可知,Flow比较被动,在没有收集数据之前,上下游是互不感知的,管道并没有建起来。
而现在我们有个场景:

需要将管道提前建起来,在任何时候都可以在上游生产数据,在下游取数据,此时上下游是可以感知的

先看最简单的Demo:

    fun test2() {
   
        //提前建立通道/管道
        val channel = Channel<String>()
        GlobalScope.launch {
   
            //上游放数据(放水)
            delay(200)
            val data = "放水啦"
            println("上游:data=$data ${
     Thread.currentThread()}")
            channel.send(data)
        }

        GlobalScope.launch {
   
            val data = channel.receive()
            println("下游收到=$data ${
     Thread.currentThread()}")
        }
    }

一个最基本的Channel包含如下几个元素:

  1. 创建Channel
  2. 往Channel里放数据(生产)
  3. 从Channel里取数据(消费)

在这里插入图片描述
使用
可以看出与Flow不同的是,生产者、消费者都可以往Channel里存放/取出数据,只是能否进行有效的存放,能否成功取出数据需要根据Channel的状态确定。
Channel最大的特点:

  1. 生产者、消费者访问Channel是线程安全的,也就是说不管生产者和消费者在哪个线程,它们都能安全的存取数据
  2. 数据只能被消费一次,上游发送了1条数据,只要有1个下游消费了数据,则其它下游将不会拿到此数据

2. Flow与Channel 相逢

2.1 Flow切换线程的始末

思考一种场景:需要在flow里进行耗时操作(比如网络请求),外界拿到flow对象后等待收集数据即可。 很容易我们就想到如下写法:

    fun test3() {
   
        runBlocking {
   
            //构造flow
            val flow = flow {
   
                //下游
                //模拟耗时
                thread {
    
                    Thread.sleep(3000)
                    emit("hello world ${
     Thread.currentThread()}")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值