1

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.

excing
  • 55
  • 6

1 Answers1

0

A few things to consider here.

  1. It's better only to inflate a View in the onCreateView so it gets inflated as fast as possible.
  2. Do other View-related work after the inflation in onViewCreated.
  3. You are using withContext which is not required as it is similar to async {...}.await which is used to return something after some processing inside the block. (That 'could' cause a delay, not sure though)

Since you are using a Flow where you have to handle the lifecycle, you can do something like this:

// 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)
        }
    }
}

Darshan
  • 4,020
  • 2
  • 18
  • 49