[英]Why does lifecycleScope.launch in a fragment block the UI thread?
[英]Why do time-consuming operations in `lifecycleScope.launch` block the UI thread?
我從數據庫中讀取頁面列表並顯示在RecyclerView
上。 它看起來像這樣:
視圖模型:
@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"
}
}
和章節列表片段:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
...
...
load()
return binding.root
}
private fun load() {
// Make sure we cancel the previous job before creating a new one
queryJob?.cancel()
queryJob = lifecycleScope.launch {
viewModel.getChapters().collectLatest { result ->
binding.hasChapters = true
adapter.submitData(lifecycle, result)
}
}
}
但是,在打開ChapterListFragment
時,會有短暫的延遲,很明顯是卡住了。
我認為這是由於viewModel.getChapters
方法中讀取數據庫的操作造成的,但是我是在協程中進行的,我不明白為什么仍然存在延遲。
不顯示與binding.hasChapters
關聯的ProgressBar
。
如果我將Thread.sleep(2000)
添加到getChapters
方法,則ChapterListFragment
將延遲兩秒打開。
所以我有兩個問題:
1. 為什么會這樣?
我原來的理解是在lifecycleScope.launch
中執行的代碼並沒有阻塞當前的UI線程,但是現在看來並不是這樣。
2. 如何正確刷新我的列表?
已編輯
我使用下面的代碼,然后打開ChapterListFragment
就變得流暢了:
private fun load() {
// Make sure we cancel the previous job before creating a new one
queryJob?.cancel()
queryJob = lifecycleScope.launch(Dispatchers.IO) {
Thread.sleep(300)
withContext(Dispatchers.Main) {
viewModel.getChapters().collectLatest { result ->
binding.hasChapters = true
adapter.submitData(lifecycle, result)
}
}
}
}
我先阻塞了 IO 線程 300ms,也就是打開Fragment
所需的動畫時間,所以沒有阻塞 UI 線程, ProgressBar
可以正常顯示。 然后在主線程(UI線程)獲取分頁列表並刷新,沒有滯后。
但是我覺得這種方式不好,不應該主動阻塞300毫秒,然后去取列表。
這里有幾件事要考慮。
onCreateView
中為View
充氣,以便它盡可能快地充氣。onViewCreated
膨脹后做其他與視圖相關的工作。withContext
,它不是必需的,因為它類似於async {...}.await
,它用於在塊內進行一些處理后返回某些內容。 (這“可能”會導致延遲,但不確定) 由於您使用的是必須處理生命周期的Flow
,因此您可以執行以下操作:
// Follows the inflated view's lifecycle
viewLifecycleOwner.lifecycleScope.launch {
// repeatOnLifecycle is a suspend function
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getChapters().collectLatest { result ->
binding.hasChapters = true
adapter.submitData(lifecycle, result)
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.