如何在点击事件触发时使用Kotlin Flow的retry函数实现API重试?
问题解答
Flow的retry机制能否满足该场景?
不能。Flow自带的retry()、retry(n)这类方法是自动触发重试,会在API失败后立刻(或按配置策略)重复调用,完全不需要用户手动交互。而你的场景需要等待用户主动点击重试按钮才重新发起请求,这种手动触发的逻辑,默认retry机制无法适配。
最佳实现方案
核心思路是用一个触发型Flow接收用户的重试点击事件,再将这个事件流与API请求流绑定,每次收到重试事件就重新发起API调用。具体实现如下:
1. Repository层(无需修改)
保持原有代码,它只负责提供基础API请求Flow:
fun getData(): Flow<Result<T>> = service.getData()
2. ViewModel层
新增MutableSharedFlow接收重试事件,通过flatMapLatest将事件流与API请求流结合:
class YourViewModel(private val repo: YourRepository) : ViewModel() { // 重试触发事件流,SharedFlow适合一次性事件场景 private val retryTrigger = MutableSharedFlow<Unit>(extraBufferCapacity = 1) // 组合数据流:初始请求 + 重试事件触发新请求 val data: Flow<Result<T>> = flow { emitAll(repo.getData()) // 首次进入先发起请求 }.flatMapLatest { initialResult -> if (initialResult is Result.Error) { // 请求失败时,监听重试事件,每次触发就重新请求 retryTrigger.flatMapLatest { repo.getData() }.onStart { emit(initialResult) } // 先保留错误状态,直到新请求返回 } else { // 请求成功后,仅返回成功结果,不再监听重试 flowOf(initialResult) } } // 供Fragment调用的重试方法 fun retry() { viewModelScope.launch { retryTrigger.emit(Unit) } } }
3. Fragment层
修改点击事件,调用ViewModel的retry()方法即可:
viewLifecycleOwner.lifecycleScope.launch { viewModel.data.collect { result -> when(result) { is Result.Success -> { hideRetryButton() // 填充列表逻辑 } is Result.Error -> { showRetryButton() // 显示错误提示 } } } } retryButton.setOnClickListener { viewModel.retry() }
补充说明
- 选用
MutableSharedFlow是因为重试事件是一次性的,无需保存状态,extraBufferCapacity = 1可避免Flow未收集时发送事件丢失。 flatMapLatest会在收到新重试事件时,取消可能还在进行的旧请求,确保只处理最新的一次请求,符合大多数场景需求。
内容的提问来源于stack exchange,提问作者Amr Jyniat
相关产品推荐
相关产品推荐

