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
相关产品推荐
相关产品推荐

