Callbacks in Paging library called multiple times

Viewed 1832

In may project I am using the Paging library from Android Architecture Components. Data is read from the Room db which is used as a cache and when the end of the list is reached I fetch new data from the network api with Boundary callbacks. Unfortunatelly when new updates are called from the api, it is being called 4 or more times, even with boolean flags in the method so the new data is incorrect.

From database I return Datasource.Factory:

    @Query("SELECT * FROM movieentity ORDER BY popularity DESC")
    fun allMovies(): DataSource.Factory<Int, MovieEntity>

Methods in Repository, basically I want to clear cache and lead next page from the api:

    override fun allMovies(movieBoundaryCallback: 
    MainViewModel.MovieBoundaryCallback): LiveData<PagedList<Movie>> {
        // Get data source factory from the local cache
        val dataSourceFactory = moviesDao.allMovies().map {
            MovieMapper.fromDb(
                it
            )
        }

        val config: PagedList.Config = PagedList.Config.Builder()
            .setInitialLoadSizeHint(DATABASE_PAGE_SIZE)
            .setPageSize(DATABASE_PAGE_SIZE)
            .setPrefetchDistance(0)
            .build()

        // Get the paged list
        return LivePagedListBuilder(dataSourceFactory, config)
            .setBoundaryCallback(movieBoundaryCallback)
            .build()
    }

    override suspend fun refreshMovies(page: Int) {
        withContext(Dispatchers.IO) {
            moviesDao.deleteAll()
            moviesDao.insertAll(*fetchMovies(page).toTypedArray())
        }
    }

The Boundary callback class is defined in ViewModel as innner class:

    val callback = MovieBoundaryCallback()

    // get movies saved in local db
    val movies = getMoviesUseCase.allMovies(callback)

    ...

    inner class MovieBoundaryCallback : PagedList.BoundaryCallback<Movie>() {
        private var lastRequestedPage = 1
        private var isRequestInProgress = false

        private fun refreshMoviesList() {
            if (isRequestInProgress) return

            isRequestInProgress = true
            if (isInternetAvailable(getApplication())) {
                lastRequestedPage++
                try {
                    viewModelScope.launch {
                        getMoviesUseCase.refreshMovies(lastRequestedPage)
                    }
                    isRequestInProgress = false
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }

        override fun onItemAtEndLoaded(itemAtEnd: Movie) {
            refreshMoviesList()
        }
    }

For RecyclerView's adapter I extend PagedListAdapter. Maybe the problem is because of coroutines?Please help me solve this problem or give a hint. Thanks in advance.

1 Answers

I am not so sure, but I guess the problem is because of the deleteAll() insertAll(). One of the advantages of Room database is that they are reactive, so that they can post information when the data is modified, which is a very cool feature.

The problem is that, sometimes they are TOO reactive. The deleteAll() insertAll() is always a problem with Room DB because the DB sends data after the deleteAll() and also after the insertAll(). You are gonna have to adapt your WS to the paggination DB to be able to retrieve only the desired data, and in case there could be a change inan existing entity, to be able to do a "smart insertAll()"...

Related