3

I'm using the Paging 3.0 library to load some data from the server and I'm currently facing an issue where the server returns small number of items i.e 1,2, items. What happens now is the Paging library keeps calling the same endpoint, non-stop. My issue is similar to question 1 and I guess 2 just that mine is with Paging 3.0 and the approach is different.

This is my PagingSource:

private const val STARTING_PAGE_INDEX = 0

class MyPagingSource(
    private val apiService: MyProductService,
    private val query: String
) : PagingSource<Int, ProductResponse>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ProductResponse> {
        val position = params.key ?: STARTING_PAGE_INDEX
        return try {
            val response = apiService.searchProduct(STARTING_PAGE_INDEX, query, params.loadSize)
            val products = response.data?.records ?: emptyList()
            LoadResult.Page(
                data = products,
                prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
                nextKey = if (products.isEmpty()) null else position + 1
            )
        }catch (exception: IOException){
            return LoadResult.Error(exception)
        }catch (exception: HttpException) {
            return LoadResult.Error(exception)
        }
    }
}

The PagingConfig:

override fun searchProduct(
        product: String
    ): Flow<PagingData<ProductResponse>> {
        return Pager(
            config = PagingConfig(
                pageSize = RECORDS_PER_PAGE,
                enablePlaceholders = false,
                maxSize = MAX_RECORDS_PER_PAGE
            ),
            pagingSourceFactory = { MyPagingSource(apiService, product) }
        ).flow
    }

    companion object {
        private const val RECORDS_PER_PAGE = 6
        private const val MAX_RECORDS_PER_PAGE = 20
    }

In my ViewModel, I have this:

 private var currentQueryValue: String? = null

    private var currentSearchResult: UIEvent<Flow<PagingData<ProductResponse>>>? = null

    fun searchProduct(query: String): UIEvent<Flow<PagingData<ProductResponse>>> {
        val lastResult = currentSearchResult
        if (query == currentQueryValue && lastResult != null) {
            return lastResult
        }
        currentQueryValue = query
        val newResult: Flow<PagingData<ProductResponse>> = dataSource.searchProduct(query)
            .cachedIn(viewModelScope)
        currentSearchResult = UIEvent(newResult)
        return UIEvent(newResult)
    }

And in the Fragment, I have this:

private var searchProductJob: Job? = null
private val viewModel by viewModels<MyViewModel> { viewModelProviderFactory }
....
private fun searchProduct(queryString: String) {
        searchProductJob?.cancel()
        searchProductJob = lifecycleScope.launch {
            viewModel.searchProduct(queryString.trim().toLowerCase(Locale.getDefault()))
                .getContentIfNotHandled()?.let { pagingData ->
                pagingData.collectLatest {
                    productResultAdapter.submitData(it)
                    search_product_recycler_view.scrollToPosition(0)
                }
            }
        }
    }

I would appreciate any suggestions on how to go about this :)

Mayokun
  • 1,084
  • 1
  • 8
  • 20
  • Check to make sure you are eventually returning `null` for `nextKey`. It looks like your last page always contains an item so your ending condition of products.isEmpty() never triggers. It looks like you've actually hardcoded it to always load from the same page: `apiService.searchProduct(STARTING_PAGE_INDEX,...)` – dlam Sep 15 '20 at 06:01

0 Answers0