I read list of pages from database and display on RecyclerView
. It looks like this:
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"
}
}
And ChapterListFragment:
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)
}
}
}
However, when opening ChapterListFragment
, there is a short period of lag, which is obviously stuck.
I think this is caused by the operation of reading the database in the viewModel.getChapters
method, but I do it in a coroutine, and I don't understand why there is still a lag.
The ProgressBar
associated with binding.hasChapters
is not displayed.
If I add a Thread.sleep(2000)
to the getChapters
method, then the ChapterListFragment
will be opened with a delay of two seconds.
So I have two questions:
1. Why is this happening?
My original understanding was that the code executed in lifecycleScope.launch
does not block the current UI thread, but now it seems that this is not the case.
2. How do I properly refresh my list?
EDITED
I use the following code, then opening ChapterListFragment
becomes smooth:
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)
}
}
}
}
I first blocked the IO thread for 300ms, which is the animation time required to open the Fragment
, so it does not block the UI thread, and the ProgressBar
can be displayed normally. Then get the paging list in the main thread (UI thread) and refresh it, there is no lag.
But I think this way is not good, I should not actively block for 300 milliseconds, and then go to fetch the list.