【问题标题】:android coroutines flow manual retryandroid协程流程手动重试
【发布时间】:2021-02-16 17:05:09
【问题描述】:

我在我的 viewModel 中使用 stateFlow 来获取带有密封类的 api 调用的结果,如下所示:

sealed class Resource<out T> {
    data class Loading<T>(val data: T?): Resource<T>()
    data class Success<T>(val data: T?): Resource<T>()
    data class Error<T>(val error: Exception, val data: T?, val time: Long = System.currentTimeMillis()): Resource<Nothing>()
}
class VehicleViewModel @ViewModelInject constructor(application: Application, private val vehicleRepository: VehicleRepository): BaseViewModel(application) {

    val vehiclesResource: StateFlow<Resource<List<Vehicle>>> = vehicleRepository.getVehicles().shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
}

我想在我的 UI 中建议一个按钮,这样如果 api 调用失败,用户可以重试调用。我知道流上有一个retry 方法,但它不能手动调用,因为它只在发生异常时触发。

一个常见的用例是:用户没有互联网连接,当 api 调用返回网络异常时,我向用户显示一条消息,告诉他检查其连接,然后使用重试按钮(或通过检测设备现在已连接,无论如何),我重试流程。

但我想不出一种方法来做你做不到的,比如打电话给flow.retry()。一旦发生异常,就会调用实际的retry 方法。我不想在不要求用户检查其连接的情况下立即重试,这是没有意义的。

实际上我找到的唯一解决方案是在按下重试按钮时重新创建活动,以便重置流程,但这当然对性能很糟糕。

没有流程,解决方案很简单,并且有大量示例,您只需重新启动工作,但我无法找到正确使用流程的方法。我的存储库中有本地房间数据库和远程服务之间的逻辑,并且流 api 非常好,所以我也想在这个用例中使用它。

【问题讨论】:

    标签: android kotlin-coroutines kotlin-flow


    【解决方案1】:

    我建议将vehicleResource 保留为视图模型中的一个字段,并调用一个函数来进行 API 调用以获取数据。

    private val _vehiclesResource = MutableStateFlow<Resource<List<Vehicle>>>(Resource.Loading(emptyList()))
    val vehiclesResource: StateFlow<Resource<List<Vehicle>>> = _vehiclesResource.asStateFlow()
    
    init {
        fetchVehicles()
    }
    
    private var vehicleJob : Job? = null
    
    fun fetchVehicles() {
        vehicleJob?.cancel()
    
        vehicleJob = viewModelScope.launch {
            vehicleRepository.getVehicles().collect {
                _vehiclesResource.value = it
            }
        }
    }
    

    API 将在 viewmodel 的构造函数中调用。你也可以通过视图(按钮的点击监听器)调用这个函数来获取错误状态。


    P.S:我应该提一下,您的这行代码有问题,SharedFlow 无法转换为 StateFlow。

    val vehiclesResource: StateFlow<Resource<List<Vehicle>>> = vehicleRepository.getVehicles().shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
    

    【讨论】:

    • 感谢您的回答,对 stateFlow 感到抱歉,我在编写示例时出错,我确实使用了 sharedFlow。我会尝试你的解决方案,我实际上已经考虑过了,但与 retry() 方法的简单性相比,它对我来说有点沉重。但是,如果我想保留这种架构,我没有其他解决方案,因为它缺乏更好的接缝方式。我应该将 fetchVehicles() 方法放入作业中并在重新调用之前取消它吗?否则收集只会累积,我错了吗?
    • @BenjaminLedet 您的重试策略有点特殊,我认为您找不到紧凑的解决方案(因为它取决于用户交互,而不是自动事件)。关于取消正在运行的工作,你是对的。谢谢,我编辑了我的答案。
    • @BenjaminLedet 你有没有想过这个问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2016-10-26
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 2014-11-16
    • 2022-09-22
    相关资源
    最近更新 更多