You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Flow协程修复两个API请求执行异常问题

问题原因及修复方案

核心问题

  • ViewModel中authorAndQuote Flow仅初始化一次
    你在ViewModel初始化阶段就创建了对应单次API请求的Flow,这个Flow执行完成后就不会再发射新数据。后续点击按钮调用makeApiRequest时,collect的是同一个已完成的Flow,自然无法收到新结果,这就是需要多次点击才能触发更新的根本原因。
  • 嵌套Collect的不合理性
    在第一个Flow的collect回调中直接嵌套collect第二个Flow,会导致每次第一个Flow有数据时都创建新的收集器,既可能引发资源泄漏,也会让逻辑变得混乱。

修复步骤

1. 改造ViewModel,支持每次点击触发新请求

将authorAndQuote改为函数,确保每次调用都生成新的Flow,触发新的API请求:

@HiltViewModel
class InspirationQuotesViewModel @Inject constructor(
    application: Application,
    private val postInspirationQuote: PostInspirationQuote,
    private val getImageQuotePictureItemUseCase: GetImageQuotePictureItemUseCase
) : BaseViewModel() {
    // 改为函数,每次调用返回新的请求Flow
    fun fetchAuthorAndQuote(): Flow<InspirationQuotesDetailsResponse> {
        return postInspirationQuote.build(Unit)
    }

    fun getImageAuthorFromWikipedia(name: String): Flow<WikipediaPageDetailsResponse> {
        return getImageQuotePictureItemUseCase.build(name, Unit)
    }
}

2. 优化Fragment的Flow收集逻辑

用链式调用替代嵌套collect,同时添加请求取消逻辑,避免并发请求冲突:

// 保存请求Job,用于取消未完成的请求
private var requestJob: Job? = null

private fun makeApiRequest() {
    // 取消之前的请求,防止多个请求同时执行
    requestJob?.cancel()
    requestJob = lifecycleScope.launch {
        vm.fetchAuthorAndQuote()
            .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
            // 用flatMapLatest串联两个Flow,确保新请求触发时取消旧的图片请求
            .flatMapLatest { quoteResponse ->
                // 先更新作者和引用的UI
                layout.tvAuthor.text = quoteResponse.quoteAuthor
                layout.tvQuote.text = quoteResponse.quoteText
                
                val authorName = quoteResponse.quoteAuthor
                if (authorName.isNotEmpty()) {
                    vm.getImageAuthorFromWikipedia(authorName)
                        .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
                } else {
                    flowOf(null)
                }
            }
            .collect { imageResponse ->
                layout.ivImageQuotes.visibility = View.VISIBLE
                // 根据图片响应加载对应内容
                imageResponse?.query?.pages?.entries?.first()?.value?.thumbnail?.source?.let { imageUrl ->
                    Glide.with(requireContext())
                        .load(imageUrl)
                        .into(layout.ivImageQuotes)
                } ?: run {
                    loadDefaultOfflineImage()
                }
            }
    }
}

// 抽离默认图片加载逻辑,简化代码
private fun loadDefaultOfflineImage() {
    Glide.with(requireContext())
        .load(ContextCompat.getDrawable(requireContext(), R.drawable.offline))
        .into(layout.ivImageQuotes)
}

3. 额外优化建议

  • 给Flow添加异常处理(比如catch操作符),避免单个请求失败导致整个收集流程终止
  • 可将数据转换逻辑移至ViewModel中,让Fragment仅负责UI更新,降低耦合度

内容的提问来源于stack exchange,提问作者chrisChris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 18:16:38