kotlin list替换_用Kotlin频道流替换singleliveevent

kotlin list替换

After the announcement of the StateFlow implementation this year, I got curious about the possibility to totally replace LiveData. This means one less project dependency and achieve a more independent code from Android framework.

宣布StateFlow之后 在今年的实施中,我对完全取代LiveData的可能性感到好奇。 这意味着更少的项目依赖关系,并获得了与Android框架更加独立的代码。

StateFlow is not the subject of this post but we can change the view states represented by a LiveData using a StateFlow. For SingleLiveEvent class, we need a different solution.

StateFlow不是本文的主题,但是我们可以使用StateFlow更改由LiveData表示的视图状态 对于SingleLiveEvent类,我们需要其他解决方案。

SingleLiveEvent (SingleLiveEvent)

Following the MVVM pattern, ViewModel provides the view state and events/actions to the View. In the context of LiveData, the second could be implemented using the class SingleLiveEvent. Some examples of actions are: dialog show, snack bar display, screen navigation.

遵循MVVM模式,ViewModel向视图提供视图状态和事件/动作。 在LiveData上下文中,第二个可以使用类SingleLiveEvent实现。 动作示例包括:对话框显示,小吃店显示,屏幕导航。

第一种方法:渠道 (First approach: Channel)

Channels provide a way to transfer a stream of values.

通道提供了一种传输值流的方法。

Change from val action = SingleLiveEvent<Action>() to val action = Channel<Action>(Channel.BUFFERED)

val action = SingleLiveEvent<Action>()更改为val action = Channel<Action>(Channel.BUFFERED)

and on the Activity side as simple as this:viewModel.action.onEach{ ... }.launchIn(lifecycleScope)

在“活动”这一边就这么简单: viewModel.action.onEach{ ... }.launchIn(lifecycleScope)

问题 (Problem)

Everything seemed to be working fine until I tested a configuration change that recreates my Activity. After that, the action is not executed anymore 😔

在我测试了重新创建Activity配置更改之前,一切似乎都工作正常。 之后,将不再执行该动作executed

Image for post
Bug after theme change
主题更改后的错误

The Channel attached to the Activity lifecycle coroutine scope is canceled when Activity.onDestroy() is called as a side effect of coroutine context cancellation.

当调用Activity.onDestroy()作为协程上下文取消的副作用时,将取消附加到Activity生命周期协程范围的Channel

override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
    if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
        lifecycle.removeObserver(this)
        coroutineContext.cancel()
    }
}

(Solution)

Instead of using Channel, I changed to BroadcastChannel + Flow. Similar but different. Let's see!😉

我不使用Channel ,而是改为BroadcastChannel + Flow 。 相似但不同。 让我们来看看!😉

class MyViewModel : ViewModel() {
    protected val actionSender = BroadcastChannel<Action>(Channel.BUFFERED)
    val actionReceiver = actionSender.asFlow()
}

and observing it in the Activity:

并在“活动”中对其进行观察:

private fun observeActionCommand() {
    lifecycleScope.launchWhenStarted {
        viewModel.actionReceiver.collect {
            // TODO
        }
    }
}

广播频道与频道(BroadcastChannel vs Channel)

BroadcastChannel is NOT a specialization of a Channel as the name would suggest. I even found Roman Elizarov comment about this:

顾名思义, BroadcastChannel不是对Channel的专门化。 我什至发现Roman Elizarov对此有评论

Having thought about it a bit more, it looks the whole BroadcastChannel is a misnomer. They are not really channels! They are more like some kind of "hot flows".

再多考虑一下,看起来整个BroadcastChannel都是错误的。 他们不是真正的渠道! 它们更像是某种“热流”。

Using the first approach with Channel, it implements SendChannel and ReceiveChannel that gets closed when the view lifecycle scope is cancelled.

通过将第一种方法与Channel结合使用,它实现了SendChannelReceiveChannel ,它们取消视图生命周期范围时关闭

On the other hand, BroadcastChannel only implements SendChannel. A new ReceiveChannel is created to collect items from the BroadcastChanel (openSubscription) every time we launch the Flow (Flow from .asFlow). This way, only the ReceiveChannel is closed when the scope is cancelled and the BroadcastChannel remains opened.

另一方面, BroadcastChannel仅实现SendChannel。 每次我们启动流(来自.asFlow的流)时,都会创建一个新的ReceiveChannel来从BroadcastChanel(openSubscription)中收集项目。 这样,在取消作用域并且广播频道保持打开状态时,仅关闭接收频道

Every flow collector will trigger a new broadcast channel subscription.fun <T> BroadcastChannel<T>.asFlow()

每个流收集器将触发一个新的广播频道订阅。 fun <T> BroadcastChannel<T>.asFlow()

启动与启动时 (launch vs launchWhenStarted)

LiveData only emits when the LifecycleOwner is on active state (State.STARTED).

LifecycleOwner是激活状态(State.STARTED)LiveData仅发射。

If we use launch on our solution, we may have the problematic scenario:

如果在解决方案上使用启动,则可能会遇到问题:

  • App in backgroundOur screen has gone to saved state but our action continues to be consumed. It may lead to an exception if we are trying to commit a fragment transaction for example.

    后台应用屏幕已进入保存状态,但我们的操作仍在继续。 例如,如果我们尝试提交片段事务,可能会导致异常。

Using launchWhenStarted we achieve the same LiveData behaviour that pauses its consumption if the lifecycle state is "lower" than Started.

如果生命周期状态“低于” Started,则使用launchWhenStarted可以实现相同的LiveData行为,该行为将暂停其使用。

结论 (Conclusion)

I've written about a single LiveData use case exploring some common scenarios in which it may fail, improving our solution. LiveData is really useful and easy to work with Android, but we always need to consider and learn from other solutions.

我已经写了一个LiveData用例,探讨了一些可能失败的常见场景,从而改进了我们的解决方案。 LiveData确实非常有用,并且可以轻松地与Android一起使用,但是我们始终需要考虑其他解决方案并向其学习。

进一步阅读 (Further reading)

Kotlin: Diving in to Coroutines and ChannelsAmazing general article about Channels guided through a coffee shop analogy.

Kotlin:深入探讨协程和渠道关于咖啡店类比引导渠道的惊人的一般文章。

Cold flows, hot channelsDifferences between flow and channel.

冷流,热通道流与通道之间的差异。

翻译自: https://medium.com/@cesarmorigaki/replace-singleliveevent-with-kotlin-channel-flow-b983f095a47a

kotlin list替换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值