如何使用Flow协程修复两个API请求执行异常问题
问题原因及修复方案
核心问题
- ViewModel中
authorAndQuoteFlow仅初始化一次
你在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
相关产品推荐
相关产品推荐

