2

I'm using Paging 3 for the first time in a project and I'm not getting new pages appended after page 2. I'm using the TMDB API discover endpoint to get a list of movies and request more on scroll as expected. Using Retrofit for network, Gson for deserialization, Hilt for DI, and Flows to collect the data in my Fragment.

This works fine but just for 2 pages. The 3rd page is requested and received but not appended to the list and I can't scroll any further. I only notice this from my logcat.

Here's my approach.

Paging Source:

private const val STARTING_PAGE_INDEX = 1

class DiscoverMoviesPagingDataSource(private val service: MoviesService): PagingSource<Int, DiscoverMoviesDataDto>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DiscoverMoviesDataDto> {

        return try {
            val currentPage = params.key ?: STARTING_PAGE_INDEX
            val response = service.fetchDiscoverMoviesList(currentPage)
            val movies = response.body()?.data!!

            val movieResponse = mutableListOf<DiscoverMoviesDataDto>()
            movieResponse.addAll(movies)

            Log.d("MyPagingSource", "Loaded ${movies.size} items")

            LoadResult.Page(
                data = movieResponse,
                prevKey = if (currentPage == STARTING_PAGE_INDEX) null else currentPage.minus(1),
                nextKey = currentPage.plus(1)
            )

        } catch (exception: IOException) {
            LoadResult.Error(exception)
        } catch (exception: HttpException) {
            LoadResult.Error(exception)
        } catch (exception: Exception) {
            LoadResult.Error(exception)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, DiscoverMoviesDataDto>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }
}

Repository:

@ActivityScoped
class MovieRepositoryImpl @Inject constructor(
    private val moviesService: MoviesService
) : MovieRepository {

    override fun fetchDiscoverMovieList(): Flow<PagingData<DiscoverMoviesDataDto>> {

        return Pager(
            PagingConfig(
                pageSize = NETWORK_PAGE_SIZE,
                prefetchDistance = 5
            )
        ) {
            DiscoverMoviesPagingDataSource(moviesService)
        }.flow
    }
}

ViewModel:

@HiltViewModel
class MovieViewModel @Inject constructor(private val repository: MovieRepositoryImpl): ViewModel() {

    private val _movieList = repository.fetchDiscoverMovieList().cachedIn(viewModelScope)
    val movieList get() = _movieList

}

Here's how I'm observing in the fragment:

viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.movieList.collect {
                    adapter.submitData(pagingData = it)
                }
            }
        }

And lastly my paging adapter:

class MovieListAdapter(
    private val context: Context,
    private val onMovieItemClicked: (id: String) -> Unit
) : PagingDataAdapter<DiscoverMoviesDataDto, MovieListAdapter.MovieListViewHolder>(MoviesAdapterDiffer()) {

    inner class MovieListViewHolder(val binding: ItemCardMovieBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bindItem(movieItem: DiscoverMoviesDataDto) {
            binding.tvMovieName.text = movieItem.title
            binding.tvMovieDate.text = parseFriendlyDate(movieItem.release_date)
            binding.cardItemMovie.setOnClickListener {
                onMovieItemClicked(movieItem.id.toString())
            }

            Glide.with(context)
                .load(Constants.BACKDROP_BASE_URL + movieItem.poster_path)
                .into(binding.ivMovieImage)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieListViewHolder {
        val binding =
            ItemCardMovieBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return MovieListViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MovieListViewHolder, position: Int) {
        getItem(position)?.let { holder.bindItem(it) }
    }


    class MoviesAdapterDiffer() : DiffUtil.ItemCallback<DiscoverMoviesDataDto>() {
        override fun areItemsTheSame(
            oldItem: DiscoverMoviesDataDto,
            newItem: DiscoverMoviesDataDto
        ): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(
            oldItem: DiscoverMoviesDataDto,
            newItem: DiscoverMoviesDataDto
        ): Boolean {
            return oldItem == newItem
        }
    }
}

I have tried various implementations of the paging source, Pager and the calling source (either viewmodel or repository). Also changed Moshi to Gson incase that helped. Nothing has helped so far

Judah
  • 31
  • 4

0 Answers0