android - StateFlow 设置值会丢弃一些事件,但更新不会

标签 android kotlin kotlin-coroutines

在我的应用中,我有一个 UIState 密封类来表示 UI 状态。

sealed class UIState<T> {
    class ShowLoading<T> : UIState<T>()
    class HideLoading<T> : UIState<T>()
    class ShowEmptyData<T> : UIState<T>()
    class ShowData<T>(val data: T) : UIState<T>()
    class ShowError<T>(val errorUIState: ErrorUIState) : UIState<T>()
}

因此,我的 viewmodel 代码是:

 someRequest.apply { response ->
     when (response) {
         is ApiResponse.Success -> {
             _uiStateFlow.value = (UIState.HideLoading()) // First, hide the loading
             // Do some work
             _uiStateFlow.value = (UIState.ShowData(data))
         }
         is ApiResponse.Error -> {
             _uiStateFlow.value = (UIState.ShowError(error))
         }
     }
 }

在这种情况下,大多数时候我的 hideLoading 状态不会收集,它会下降,因为成功/错误状态会在 hideLoading 之后立即出现,而我的 UI 不会收集它。例如。如果我将延迟成功/错误状态设置为 100 毫秒,hideLoading 将从 UI 中收集。 我使用 collect 而不是 collectLatest

但后来我发现,当我更改设置值部分以更新 {} 时,UI 会收集所有状态。

someRequest.apply { response ->
    when (response) {
        is ApiResponse.Success -> {
            _uiStateFlow.update { (UIState.HideLoading()) } // First, hide the loading
            // Do some work
            _uiStateFlow.update { (UIState.ShowData(data)) }
        }
        is ApiResponse.Error -> {
            _uiStateFlow.update { (UIState.ShowError(error)) }
        }
   }
}

那么 .value 和 update 之间有什么区别,为什么这个效果很好?谢谢。

附言我也使用过 emit()。在底层,它与 .value 相同,只是一个挂起函数。

最佳答案

StateFlow documentation 中所述:

Updates to the value are always conflated.

Conflated 意味着如果值的发布速度比它们的收集速度快,那么收集器只会获得最新的结果。这允许始终将值发布到 StateFlow,而无需等待收集旧值。

至于为什么update允许加载状态通过,我怀疑这只是因为原子更新通常需要更长的时间,因此收集器通常会赢得比赛,在您的特定测试设备上的这种特定情况下。这不是确保收集器获得所有中间值的可靠解决方案。

我不明白为什么您首先需要 HideLoading 状态。您的 UI 可以在接收到要显示的数据时自动隐藏加载状态。从逻辑上讲,当数据返回时加载完成。

如果你真的需要这个 HideLoading 状态,你应该使用带有 replay 的 SharedFlow足够大的值以确保它不会被跳过。但这会带来其他问题,比如收集者可能会得到过时的数据来展示,因为这些数据正在向他们重放。

旁注,您的密封类可能应该是一个密封接口(interface),因为它没有状态,并且它的没有状态的子级可以是 object s 的泛型类型为 Nothing因此,您不必为了使用它们而继续实例化它们,也不必在它们根本不持有该类型时不必要地指定泛型类型。由于其他的是数据包装器,它们也可能是 data类。像这样:

sealed interface UIState<out T> {
    object ShowLoading : UIState<Nothing>
    // object HideLoading : UIState<Nothing>
    object ShowEmptyData : UIState<Nothing>
    data class ShowData<out T>(val data: T) : UIState<T>
    data class ShowError(val errorUIState: ErrorUIState) : UIState<Nothing>
}

关于android - StateFlow 设置值会丢弃一些事件,但更新不会,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72088857/

相关文章:

java - 如果 ViewModel 类的 minifyEnabled 为 true,则出现 RuntimeException

kotlin - 在kotlin中使用elvis运算符时如何否定 bool 表达式?

android - Kotlin Coroutines - 从 Coroutine 返回一个值而不阻塞主线程 Android

android - 使用 Dispatchers.IO 进行 Kotlin 协程测试

android - MainActivity 类型未定义 getSupportLoaderManager()

java - 是否可以用按钮隐藏键盘?

android - 以编程方式更改 AppBarLayout 主题

database - Android:当绑定(bind)到的数据库发生变化时,listviews 能否动态更新 UI 输出?

android - Ljava/net/URL 类中没有字段主机

Android Kotlin 协程在严格模式下崩溃