如何在Kotlin协程中调用同一函数并实现同步API与异步UI更新?
问题描述
需求是先在UI中显示带加载状态的列表条目,等API请求成功返回后,处理列表数据并更新RecyclerView Adapter。目前在Fragment的两个协程中分别监听Flow,收到数据后调用updateAndSetAdapterList函数更新列表,但现在希望改用同步的API调用函数,同时保证从同步函数中触发的UI更新操作能异步执行。
当前Fragment中的调用代码:
lifecycleScope.launch(Dispatchers.IO) { // Videos viewModel.dashboardVideosFlow.collectLatest { flow -> when (flow) { ... is VideosFlow.DataReceived -> { val row = SectionVideosBodyData(flow.videos) updateAndSetAdapterList(1, row) } } } } lifecycleScope.launch(Dispatchers.IO) { // Questions viewModel.dashboardQuestionsFlow.collectLatest { flow -> when (flow) { .... is QuestionsFlow.DataReceived -> { val row = SectionQuestionsBodyData(flow.questions) updateAndSetAdapterList(3, row) } } } }
当前的updateAndSetAdapterList函数实现:
private suspend fun updateAndSetAdapterList(indexAt: Int? = null, row: BaseAdapterData? = null) { lifecycleScope.launch(Dispatchers.IO) { val newRows = mutableListOf<BaseAdapterData>() newRows.addAll(rows) if (indexAt != null && row != null) { newRows.removeAt(indexAt) newRows.add(indexAt, row) } withContext(Dispatchers.Main) { dashboardAdapter.submitList(newRows) } rows = newRows } } }
解决方案
1. 给列表加线程安全保护
先给Fragment添加一个Mutex,用来管控rows列表的读写操作,避免多协程并发修改引发异常:
private val rowsMutex = Mutex() private var rows = mutableListOf<BaseAdapterData>()
2. 重构列表更新函数
优化updateAndSetAdapterList,确保列表修改线程安全,同时UI更新异步执行:
private fun updateAndSetAdapterList(indexAt: Int, row: BaseAdapterData) { // 后台线程处理列表修改,不阻塞主线程 lifecycleScope.launch(Dispatchers.IO) { rowsMutex.withLock { // 安全拷贝并修改列表 val newRows = rows.toMutableList() newRows.removeAt(indexAt) newRows.add(indexAt, row) // 更新原列表 rows = newRows // 切回主线程异步更新UI withContext(Dispatchers.Main) { dashboardAdapter.submitList(newRows) } } } }
3. 同步API调用的正确姿势
同步API函数必须放在Dispatchers.IO线程的协程中执行,避免阻塞主线程;拿到数据后直接调用更新函数即可:
// 处理Videos同步API请求 lifecycleScope.launch(Dispatchers.IO) { try { // 同步调用API,IO线程执行不卡UI val videos = fetchVideosSync() val row = SectionVideosBodyData(videos) // 触发列表更新,UI操作自动异步执行 updateAndSetAdapterList(1, row) } catch (e: Exception) { // 处理请求异常,比如替换为错误状态条目 withContext(Dispatchers.Main) { val errorRow = SectionVideosErrorData() updateAndSetAdapterList(1, errorRow) } } } // 处理Questions同步API请求 lifecycleScope.launch(Dispatchers.IO) { try { val questions = fetchQuestionsSync() val row = SectionQuestionsBodyData(questions) updateAndSetAdapterList(3, row) } catch (e: Exception) { withContext(Dispatchers.Main) { val errorRow = SectionQuestionsErrorData() updateAndSetAdapterList(3, errorRow) } } }
核心说明
- 线程安全:用
Mutex.withLock包裹列表读写,确保同一时间只有一个协程修改rows,杜绝并发问题。 - 异步UI更新:列表修改在IO线程完成,UI更新通过
withContext(Dispatchers.Main)切回主线程,全程不阻塞UI。 - 同步API适配:同步API必须放在IO线程协程中调用,否则会直接卡住主线程导致界面无响应。
内容的提问来源于stack exchange,提问作者murat_yuksektepe
相关产品推荐
相关产品推荐

