BroadcastChannel全解析

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

前言

对于 channel 而言,更多面向的是一对一的情况,因为一旦 receive() 后,该数据就被获取了,后续再 receive() ,也无法拿到之前的数据。但是,类似的,Flow 与 sharedflow 是相对应的,与 channel 相对应的是 BroadcastChannel。

使用

        GlobalScope.launch {
            List(2) { index ->
                launch {
                    val receiver = channel.openSubscription()
                    val iterator = receiver.iterator()
                    while (iterator.hasNext()) {
                        println("channel $index 消费了 ${iterator.next()}")
                    }
                }
            }
            delay(1000)
            
            launch {
                (1..3).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                }
            }
        }
复制代码

日志输出:

I/System.out: channel 生产了 1
I/System.out: channel 1 消费了 1
I/System.out: channel 0 消费了 1
I/System.out: channel 生产了 2
I/System.out: channel 1 消费了 2
I/System.out: channel 0 消费了 2
I/System.out: channel 生产了 3
I/System.out: channel 1 消费了 3
I/System.out: channel 0 消费了 3
复制代码

BroadcastChannel 并不是直接通过 receive() 进行获取数据,而且先执行 openSubscription() 获得 ReceiveChannel,然后在通过 ReceiveChannel 去进行 receive() ,在这里我为了方便遍历就使用了 iterator()

有一点要额外注意,就是 sharedflow 和 stateflow 都是先 emit() 数据,再进行 collect() 的,而 BroadcastChannel 是刚好相反,是先注册,然后再获取数据的,所以,当 BroadcastChannel 已经 send() 后,再执行 openSubscription() 是无法获取数据的。下面我们通过这个小栗子验证下:


        val channel = BroadcastChannel<Int>(3)

        GlobalScope.launch {
            launch {
                val receiver = channel.openSubscription()
                val iterator = receiver.iterator()
                while (iterator.hasNext()) {
                    println("channel 1 消费了 ${iterator.next()}")
                }
            }
            delay(1000)

            launch {
                (1..3).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                }
            }

            delay(1000)

            launch {
                val receiver = channel.openSubscription()
                val iterator = receiver.iterator()
                while (iterator.hasNext()) {
                    println("channel 2 消费了 ${iterator.next()}")
                }
            }
        }
复制代码

日志输出:

I/System.out: channel 生产了 1
I/System.out: channel 生产了 2
I/System.out: channel 1 消费了 1
I/System.out: channel 生产了 3
I/System.out: channel 1 消费了 2
I/System.out: channel 1 消费了 3
复制代码

通过日志我们会发现 channel 2 是一个日志输出都没有,所以这就验证了我们的猜想。

cancel()

对于 Channel 而言,只要 cancel()close() 之后,再进行 send() 或者 receive(),就会抛出异常,不过,对于 BroadcastChannel,其中一个 ReceiveChannel 进行 cancel() 操作,并不会影响其它 ReceiveChannel:

        val channel = BroadcastChannel<Int>(3)

        GlobalScope.launch {
            launch {
                val receiver = channel.openSubscription()
                println("channel 1 消费了 ${receiver.receive()}")
                receiver.cancel()
            }
            launch {
                val receiver2 = channel.openSubscription()
                val iterator2 = receiver2.iterator()
                while (iterator2.hasNext()) {
                    println("channel 2 消费了 ${iterator2.next()}")
                    delay(500)
                }
            }
            delay(1000)

            launch {
                (1..3).forEach { value ->
                    println("channel 生产了 $value")
                    channel.send(value)
                }
            }
        }
复制代码

日志输出:

I/System.out: channel 生产了 1
I/System.out: channel 1 消费了 1
I/System.out: channel 生产了 2
I/System.out: channel 2 消费了 1
I/System.out: channel 生产了 3
I/System.out: channel 2 消费了 2
I/System.out: channel 2 消费了 3
复制代码

参数说明

public fun <E> BroadcastChannel(capacity: Int): BroadcastChannel<E>
复制代码

BroadcastChannel 的构造方法只有一个参数 capacity,它是容量配置,当其赋值大于等于 1 的时候,就是设置相应的容量,当其小于 1 的时候,进行额外处理,并有相应的常数:

  • RENDEZVOUS:值为 0,抛出 IllegalArgumentException 异常,内容为:"Unsupported 0 capacity for BroadcastChannel"。
  • CONFLATED:值为 -1,创建一个 合并 channel。
  • BUFFERED:值为 -2,判断系统有没有设置该 kotlinx.coroutines.channels.defaultBuffer 的默认值,假如有的话,就将该值设置为容量值,假如没有的话,就设置默认值为 64 容量。
  • UNLIMITED:值为 Int.MAX_VALUE,抛出 IllegalArgumentException 异常,内容为:"Unsupported UNLIMITED capacity for BroadcastChannel"。

broadcast()

针对于 Channel,有一种比较简便的方式转为 BroadcastChannel:

val channel = Channel<Int>().broadcast(3)
复制代码
分类:
Android