Kotlin流:使用SharedFlow和StateFlow处理状态

w41d8nur  于 2023-11-21  发布在  Kotlin
关注(0)|答案(1)|浏览(131)

我有这样一个用例,其中来自数据源的响应保证被消耗,并且在配置更改后不会丢失,但在已经消耗时也不会重复。
StateFlow似乎是最好的候选者,因为它保存了最后一个发出的值,但在以下场景中可能会出现问题:
1.数据源响应在每个请求上都有相同的错误(可能是由于本地缓存中没有数据或网络问题。
由于StateFlow不发出相同的值,这可能会出现问题,在对数据源发出新请求后,如果先前的状态也是错误,则错误状态(可以是对话框)将不会再次显示在UI中。
SharedFlow与已配置的replay一起使用

MutableSharedFlow<T>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)

字符串
这将表现得像LiveDatarepeatOnLifecycle,但在以下场景中有自己的问题。
假设我们有这些状态

sealed class AssetState {
    data class FetchAssetLoading(val assetList: List<AssetMinDataDomain>?) : AssetState()
    data class FetchAssetSuccess(val assetList: List<AssetMinDataDomain>) : AssetState()
    data class FetchAssetFailed(val msg: String, val assetList: List<AssetMinDataDomain>?) : AssetState()
}


onCreateonViewCreated内部

// Initial fetch
fetchData(param1, param2)

// Collecting the result of fetch data 
with(viewModel) {
   viewLifecycleOwner.collectShared(assetState, ::onAssetStateChanged)
}


扩展函数

inline fun <T : Any, L : SharedFlow<T>> LifecycleOwner.collectShared(
    sharedFlow: L,
    crossinline function: (T) -> Unit,
    lifecycleState: Lifecycle.State = Lifecycle.State.STARTED
) {
    lifecycleScope.launch {
        repeatOnLifecycle(lifecycleState) {
            sharedFlow.collect { t -> function(t) }
        }
    }
}


当发生配置更改时,当LifeCycle至少达到STARTED时,fetchData(param1, param2)将被触发,其结果将被收集,但由于我们也使用repeatOnLifecycle,因此将有两次数据发射。一次是作为请求fetchData的结果,这是更更新的,另一次是作为repeatOnLifecycle的结果,这可能是过时的。唯一的解决方案是我目前看到的是添加一个空状态或没有状态,然后在UI中接收到成功状态或失败状态后手动设置它,但似乎不是最好的解决方案,而且太重复了。有没有更好的方法来满足上述要求?谢谢
P.S
如果我在SharedFlow中删除repeat = 1,它将解决这个问题,但是如果活动转到onStop,例如,用户在获取数据时按主页按钮,然后返回到应用程序,导致onStartonResume触发,那么我们将丢失该事件及其结果。

yzxexxkh

yzxexxkh1#

到目前为止,要使用流执行单个事件,请使用Channel并将其转换为常规冷流,如下所示
ViewModel

private val _assetState = Channel<AssetState>()

val assetState = _assetState.receiveAsFlow()

字符串
使用send来发射

// Some method 
_assetState.send(AssetState.FetchLoading())


收集获取数据的结果

with(viewModel) {
   viewLifecycleOwner.collectFlow(assetState, ::onAssetStateChanged)
}


更新扩展功能

inline fun <T : Any, L : Flow<T>> LifecycleOwner.collectFlow(
    flow: L,
    crossinline function: (T) -> Unit,
    lifecycleState: Lifecycle.State = Lifecycle.State.STARTED
) {
    lifecycleScope.launch {
        repeatOnLifecycle(lifecycleState) {
            flow.collect { t -> function(t) }
        }
    }
}


请注意,使用这种方法有一个微妙的问题,如果Channel发送了事件,但消费者突然根据设置的生命周期进入后台,您仍然可能错过事件。
参考:https://medium.com/androiddevelopers/viewmodel-one-off-event-antipatterns-16a1da869b95

相关问题