【问题标题】:MutableStateFlow events being overwrittenMutableStateFlow 事件被覆盖
【发布时间】:2021-08-11 18:19:03
【问题描述】:

在 MyViewModel 中,使用 MutableStateFlow 将事件传输到片段。 当 MutableStateFlow 的值改变时,早期的值在协程内被覆盖。所以从来没有被片段接收到。

internal class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val myMutableStateFlow = MutableStateFlow<MySealedClass>(MySealedClass.Dummy1())
    private fun getData() {
        viewModelScope.launch {
            //yield()
            myMutableStateFlow.value = MySealedClass.Dummy2()
            myMutableStateFlow.value = MySealedClass.Dummy3()
        }
    }
}

internal class MyFragment : Fragment(){
    private var uiStateJob: Job? = null
    override fun onStart() {
        super.onStart()
        uiStateJob = lifecycleScope.launch {
           myViewModel.getUiFlow().collect {
              //do something
           }
       }
    }
}

如果对 yield() 进行了注释,则 Fragment 永远不会收到 Dummy2 事件。虽然收到了 Dummy 3。 如果 yield() 未注释 Dummy2 和 3 都收到。 如果在协程之外改变了状态值,那么 Dummy2 和 Dummy3 都会被接收。

我需要以可预见的方式接收片段中的所有事件。 这种行为有合理的理由吗?

【问题讨论】:

    标签: android kotlin coroutine stateflow


    【解决方案1】:

    StateFlow 表示一个状态。每个事件在技术上都是一个新的最新状态值,使以前的状态过时。这种类型的流程适用于只有最新状态才重要的情况,因为它的事件是合并的。来自文档:

    对值的更新总是合并在一起的。因此,慢速收集器会跳过快速更新,但始终收集最近发出的值。

    根据您的评论进行编辑:yield() 是一个暂停功能,可强制暂停当前协程。因此,它为另一个协程提供了一个机会,可以在下一个暂停点之前继续前进,这就是为什么在这种情况下,在设置(并发出)第一个值之前,collect 已经“准备好”。

    但是你不应该依赖它,因为它很脆弱:如果另一个协程被修改并且通过调用其他挂起函数具有额外的挂起点,它可能无法到达 collect 调用,你会回到另一个行为。

    如果您始终需要所有事件,您有多种选择:

    • 切换到冷流,只有在您收集时才会开始
    • 使用Channel(带或不带缓冲区)
    • 使用SharedFlow 并使用onSubscription 触发事件开始

    【讨论】:

    • 这是有道理的。但是行为不应该是一致的吗?似乎如果在协程内部改变了状态,那么只会发生合并。如果不在协程内,则接收连续状态而不进行合并。我也不清楚 yield() 语句是如何改变这种情况的。
    • 它在合并事件的方式上不必是确定性的或一致的,因为在这方面没有设计保证。我编辑了我的帖子来解释产量。
    猜你喜欢
    • 2017-11-07
    • 2021-06-13
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 2014-09-02
    • 2012-01-20
    相关资源
    最近更新 更多