【问题标题】:Consumable channel耗材渠道
【发布时间】:2018-06-08 21:26:01
【问题描述】:

用例

使用来自ReceiveChannel<T>T 项的Android 片段。消费后,Ts 应从 ReceiveChannel<T> 中删除。

我需要一个ReceiveChannel<T> 来支持从中消费物品。它应该起到 FIFO 队列的作用。

我目前从我的用户界面附加到频道,例如:

launch(uiJob) { channel.consumeEach{ /** ... */ } }

我通过调用uiJob.cancel() 分离。

期望的行为:

val channel = Channel<Int>(UNLIMITED)

channel.send(1)
channel.send(2)
// ui attaches, receives `1` and `2`
channel.send(3) // ui immediately receives `3`
// ui detaches
channel.send(4)
channel.send(5)
// ui attaches, receiving `4` and `5`

不幸的是,当我从频道分离时,频道已关闭。这会导致.send(4).send(5) 抛出异常,因为通道已关闭。我希望能够从频道中分离并让它保持可用。我该怎么做?

Channel&lt;Int&gt;(UNLIMITED) 非常适合我的用例,except 在取消订阅时关闭频道。我希望频道保持开放。这可能吗?

【问题讨论】:

    标签: kotlin kotlinx.coroutines


    【解决方案1】:

    Channel.consumeEach 方法调用 Channel.consume 方法,该方法在文档中有这一行:

    确保给定块消耗给定通道中的所有元素在块执行后始终调用取消

    所以解决方案就是不使用consume[Each]。例如你可以这样做:

    launch(uiJob) { for (it in channel) { /** ... */ } }

    【讨论】:

    • 这行得通。这里的缺点是接收器可以关闭通道,这是不希望的行为
    • 对不起,你这是什么意思?
    • 我希望ReceiveChannel&lt;E&gt; 是只读的,但事实并非如此!能够关闭它使其可变
    • 我猜你可以不用close方法把它包装在另一个界面中?
    【解决方案2】:

    您可以使用BroadcastChannel。但是,您需要指定一个有限的大小(例如1),因为BroadcastChannel 不支持UNLIMITED0(用于rendez-vous)。

    您还可以使用ConflatedBroadcastChannel,它始终为新订阅者提供最新的价值,就像LiveData 所做的那样。

    顺便说一句,如果您的新 Fragment 实例只收到最新值,这有什么大不了的吗?如果没有,那么就使用ConflatedBroadcastChannel。否则,BroacastChannels 中没有一个可能适合您的用例(尝试一下,看看您是否得到了您正在寻找的行为)。

    【讨论】:

    • 我已经有一个ConflatedBroadcastChannel&lt;ViewState&gt;。需要一个“消耗性”通道来向 UI 发送副作用,例如显示敬酒或振动设备
    • 您可以向您的ViewState 添加一个markHandled() 函数,该函数翻转一个公开只读的hasBeenHandled 布尔属性,并且在尝试处理该事件之前,您检查它没有已经用 if 处理了。
    猜你喜欢
    • 2017-07-15
    • 2019-10-24
    • 2017-11-19
    • 1970-01-01
    • 2012-11-02
    • 2021-12-03
    • 2016-10-09
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多