Channel 是什么?

前言

Flow 是一种冷流,只有 collect 的时候才会产生数据,而 SharedFlow、StateFlow 虽然是热流,但是,每次 collect 的时候,要么是只能获取最新值,要么把之前存储的值重新都发一遍。有没有一种热流,能更像生产者与消费者,一个专门生产,一个专门消费,答案当然是有的,那便是 Channel。

Channel 的使用

        GlobalScope.launch {
            launch {
                (1..3).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                    delay(100)
                }
            }
            launch {
                repeat(3){
                    delay(1000)
                    val value = channel.receive()
                    println("channel 消费了 $value")
                }
            }
        }

日志:

I/System.out: channel 生产了 1
I/System.out: channel 消费了 1
I/System.out: channel 生产了 2
I/System.out: channel 消费了 2
I/System.out: channel 生产了 3
I/System.out: channel 消费了 3

容量

在这里,我特意让生产速度慢于消费速度,但是会发现,即使如此,还是生产一个,消费一个,这说明了 channel 内部是有容量的,所以需要等到消费者取走后才会生产继续生产,若我们需要扩大该容量,可以在创建 channel 的时候,赋予其初始容量:

val channel = Channel<Int>(2)

参数说明

其实,容量的配置也只是其参数配置的一部分,下面我们来完整看看其说明:

public fun <E> Channel(
    capacity: Int = RENDEZVOUS,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
    onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E>
  • capacity:容量配置,当其赋值大于等于 1 的时候,就是设置相应的容量,当其小于1 的时候,进行额外处理,并有相应的常数:
    • RENDEZVOUS:默认值,值为 0,标识没有缓冲区。
    • CONFLATED:值为 -1,创建一个 合并 channel。
    • BUFFERED:值为 -2,对于支持挂起的 channel,设置其容量为 64,对于不支持挂起的 channel,其容量为 1。
    • UNLIMITED:值为 Int.MAX_VALUE,设置为无限容量的 channel。
  • onBufferOverflow:当超过缓冲区的时候,如何进行处理:
    • SUSPEND:进行挂起操作。
    • DROP_OLDEST:不挂起,直接移除最旧的值。
    • DROP_LATEST:不挂起,直接移除最新的值。
  • onUndeliveredElement:当生产数据后,因为其它原因导致未能交付给消费者,这些数据则会进入该函数。这样要特别注意,因为 DROP_OLDEST 和 DROP_LATEST 丢失的数据并不会进入该函数,一般为 cancel()close() 或者出现异常之类的,才会进入该函数。

下面,我们写个小栗子来说明这个参数的作用:

        val channel = Channel<Int>(2, BufferOverflow.DROP_OLDEST){ value ->
            println("$value 没有被正常消费")
        }

        GlobalScope.launch {
            launch {
                (1..5).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                    delay(100)
                }
            }
            launch {
                repeat(5){
                    delay(1000)
                    val value = channel.receive()
                    println("channel 消费了 $value")
                }
            }
        }

日志输出:

I/System.out: channel 生产了 1
I/System.out: channel 生产了 2
I/System.out: channel 生产了 3
I/System.out: channel 生产了 4
I/System.out: channel 生产了 5
I/System.out: channel 消费了 4
I/System.out: channel 消费了 5

这里可以看出,生产了 1、2、3、4、5,但是只消费了 4、5,至于 1、2、3 都因为 DROP_OLDEST 被丢弃了,并且没有回调到 onUndeliveredElement。

我们稍微改下,看看另外一个🌰:

        val channel = Channel<Int>(2, BufferOverflow.DROP_OLDEST){ value ->
            println("$value 没有被正常消费")
        }

        GlobalScope.launch {
            launch {
                (1..5).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                    delay(100)
                }
            }
            launch {
                delay(1000)
                channel.cancel()
            }
        }

日志输出:

I/System.out: channel 生产了 1
I/System.out: channel 生产了 2
I/System.out: channel 生产了 3
I/System.out: channel 生产了 4
I/System.out: channel 生产了 5
I/System.out: 4 没有被正常消费
I/System.out: 5 没有被正常消费

由于 cancel() 引起的关闭,会让 4、5 进入 onUndeliveredElement 回调中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值