Android协程流手动重试

5
我在我的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非常好,所以我也想在这种情况下使用它。

1个回答

4

我建议将vehicleResource作为ViewModel的一个字段,并调用一个函数来发起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)

谢谢你的回答,抱歉我在写示例时犯了一个错误,我确实使用了sharedFlow。我会尝试你的解决方案,我其实也考虑过它,但与retry()方法的简单性相比,它似乎有点重。但是如果我想保持这种架构,似乎没有其他解决方案。我应该将fetchVehicles()方法放在一个作业中,并在重新调用之前取消它吗?否则,收集器将继续累积,我错了吗? - Benjamin Ledet
1
@BenjaminLedet,你的重试策略有点特别,我认为你可能找不到一个紧凑的解决方案(因为它取决于用户交互,而不是自动化事件)。关于取消正在运行的作业,你是正确的。谢谢,我已经编辑了我的回答。 - beigirad
@BenjaminLedet 你最终解决了这个问题吗? - George

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接