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

lifecycleScope.launch中耗时操作为何阻塞UI线程?列表刷新正确写法

我从数据库读取分页章节数据并展示在RecyclerView上,相关实现代码如下:

ViewModel层实现

@HiltViewModel
class BookDetailViewModel @Inject internal constructor(
    savedStateHandle: SavedStateHandle,
    private val bookRepository: BookRepository,
    private val chapterRepository: ChapterRepository,
) : ViewModel() {
    private var currentResult: Flow<PagingData<Chapter>>? = null

    val bookID: Long = savedStateHandle.get<Long>(BOOK_ID_SAVED_STATE_KEY)!!

    val book = bookRepository.getBook(bookID)

    suspend fun getChapters(): Flow<PagingData<Chapter>> {
        val lastChapterID = book.first().lastChapterID
        val newResult = chapterRepository
            .getChapters(bookID, lastChapterID)
            .cachedIn(viewModelScope)
        currentResult = newResult
        return newResult
    }

    companion object {
        private const val BOOK_ID_SAVED_STATE_KEY = "bookID"
    }
}

ChapterListFragment层实现

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    ...
    ...
    
    load()

    return binding.root
}
private fun load() {
    // 新建协程任务前先取消上一个任务
    queryJob?.cancel()
    queryJob = lifecycleScope.launch {
        viewModel.getChapters().collectLatest { result ->
            binding.hasChapters = true
            adapter.submitData(lifecycle, result)
        }
    }
}

问题现象

打开ChapterListFragment时存在明显的短时间卡顿,初步怀疑卡顿来自viewModel.getChapters()方法中的数据库读取操作,但该逻辑放在协程中执行仍会触发卡顿;和binding.hasChapters绑定的ProgressBar也无法正常显示。如果在getChapters方法中加入Thread.sleep(2000),ChapterListFragment的打开会直接延迟2秒。

待解决疑问

  • 出现该现象的原因是什么?
    原本认知里lifecycleScope.launch中执行的代码不会阻塞当前UI线程,但实际表现和预期不符。
  • 如何正确实现列表的刷新逻辑?

编辑补充

尝试修改为如下代码后,打开ChapterListFragment变得流畅:

private fun load() {
    // 新建协程任务前先取消上一个任务
    queryJob?.cancel()
    queryJob = lifecycleScope.launch(Dispatchers.IO) {
        Thread.sleep(300)
        withContext(Dispatchers.Main) {
            viewModel.getChapters().collectLatest { result ->
                binding.hasChapters = true
                adapter.submitData(lifecycle, result)
            }
        }
    }
}

该修改先在Dispatchers.IO线程阻塞300ms,等待Fragment打开所需的转场动画时长,避免阻塞UI线程,此时ProgressBar可以正常显示;之后切换到Dispatchers.Main(UI线程)获取分页列表并刷新,就没有卡顿了。但该实现并不合理,不应该主动阻塞300ms再去拉取列表数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:12:27