Milan Thakor
Milan Thakor

Reputation: 103

Caching is not working in Android Paging 3

I have implemented application using codelabs tutorial for new Paging 3 library, which was release week ago. The problem is application is not working in offline mode. It does not retrieve data from Room database.

Tutorial Repo link :- https://github.com/googlecodelabs/android-paging

Code:-

  1. RepoDao.kt

    @Dao
    interface RepoDao {
    
       @Insert(onConflict = OnConflictStrategy.REPLACE)
       suspend fun insertAll(repos: List<Repo>)
    
       @Query("SELECT * FROM repos WHERE " +
        "name LIKE :queryString OR description LIKE :queryString " +
        "ORDER BY stars DESC, name ASC")
       fun reposByName(queryString: String): PagingSource<Int, Repo>
    
       @Query("DELETE FROM repos")
       suspend fun clearRepos()
    }
    
  2. GithubRepository.kt

    class GithubRepository(
        private val service: GithubService,
        private val database: RepoDatabase
    ) {
       fun getSearchResultStream(query: String): Flow<PagingData<Repo>> {
    
          val dbQuery = "%${query.replace(' ', '%')}%"
          val pagingSourceFactory = { database.reposDao().reposByName(dbQuery) }
    
          return Pager(
             config = PagingConfig(pageSize = NETWORK_PAGE_SIZE),
             remoteMediator = GithubRemoteMediator(
                    query,
                    service,
                    database
            ),
            pagingSourceFactory = pagingSourceFactory
          ).flow
       }
    
       companion object {
           private const val NETWORK_PAGE_SIZE = 50
       }
    }
    
  3. SearchRepositoriesViewModel.kt

    @ExperimentalCoroutinesApi
    class SearchRepositoriesViewModel(private val repository: GithubRepository) : ViewModel() {
        private var currentQueryValue: String? = null
    
        private var currentSearchResult: Flow<PagingData<Repo>>? = null
    
        fun searchRepo(queryString: String): Flow<PagingData<Repo>> {
            val lastResult = currentSearchResult
            if (queryString == currentQueryValue && lastResult != null) {
                return lastResult
            } 
            currentQueryValue = queryString
            val newResult: Flow<PagingData<Repo>> = repository.getSearchResultStream(queryString).cachedIn(viewModelScope)
            currentSearchResult = newResult
            return newResult
        }
    
    } 
    
  4. SearchRepositoriesActivity.kt

    @ExperimentalCoroutinesApi
    class SearchRepositoriesActivity : AppCompatActivity() {
    
        .....
        private lateinit var viewModel: SearchRepositoriesViewModel
        private val adapter = ReposAdapter()
    
        private var searchJob: Job? = null
    
        // this is where adapter get flow data from viewModel
        // initially this is called with **Android** as a query
        private fun search(query: String) {
            searchJob?.cancel()
            searchJob = lifecycleScope.launch {
                viewModel.searchRepo(query).collectLatest {
                    adapter.submitData(it)
                }
            }
        }
        .....
     }
    

Output:- It is just showing the empty recyclerview when application is open in offline mode.

Upvotes: 3

Views: 7086

Answers (1)

dlam
dlam

Reputation: 3895

If you're able to share your code or how you reached that conclusion I could probably help pinpoint the problem a bit better, but the codelab does load data from Room on the branch: step13-19_network_and_database

There are two components here:

PagingSource: Provided by Room by declaring a @Query with a PagingSource return type, will create a PagingSource that loads from Room. This function is called in the pagingSourceFactory lambda in Pager which expects a new instance each call.

RemoteMediator: load() called on boundary conditions where the local cache is out of data, this will fetch from network and store in the Room db, which automatically propagates updates to PagingSource implementation generated by Room.

One other issue you might be seeing could be related to loadStateListener/Flow, essentially the codelab shows an error state by checking for CombinedLoadStates.refresh, but this always defers to the RemoteMediator's load state when available and if you want to show the locally cached data, even when RemoteMediator errors out, you'll need to disable hiding of the list in that case.

Note that you can access individual LoadState with CombinedLoadStates.source or CombinedLoadStates.mediator.

Hopefully this is enough to help you, but it's hard to guess your issue without some more concrete example / information about what you're seeing.

Edit: While the above are still good things to check for, it looks like there's an underlying issue with the library that I'm chasing down here: https://android-review.googlesource.com/c/platform/frameworks/support/+/1341068

Edit2: This is fixed now and will be released with alpha02.

Upvotes: 1

Related Questions