Ioane Sharvadze
Ioane Sharvadze

Reputation: 2158

Android Jetpack Paging 3: PagingSource with Room

I'm using latest Jetpack libraries.
Pagination3 version: 3.0.0-alpha05
Room Version : 2.3.0-alpha02

My entities have Long as PrimaryKey and Room can generate PagingSource for other than Int type.

error: For now, Room only supports PagingSource with Key of type Int.
    public abstract androidx.paging.PagingSource<java.lang.Long, com.example.myEntity>` getPagingSource();

Therefore I tried to implement my custom PagingSource, like docs suggest.

The problem is Data Refresh, since Room's generated code handles data refresh and with my code I'm not being able to handle this scenario.

Any suggestions how to implement custom PagingSource for Room that also handles Data Refresh?

Upvotes: 11

Views: 7417

Answers (1)

shoheikawano
shoheikawano

Reputation: 1175

Since you have 'refresh' scenario and using Room db, I am guessing you are using Paging3 with network+local db pattern(with Room db as local cache).

I had a similar situation with network + local db pattern. I am not sure if I understand your question correctly, or your situation is the same as the one I had, but I'll share what I did anyway.

What I was using:

  • Paging3: 3.0.0-beta01
  • Room: 2.3.0-beta02

What I did was let Room library to create PagingSource (with the key of Int), and let RemoteMediator handle all the other cases, such as fetching the data from network when refreshing and/or appending, and inserting them into db right after fetch success.

My dao function for creating PagingSource from Room Library:

@Query("SELECT * FROM article WHERE isUnread = 1")
fun getUnreadPagingSource(): PagingSource<Int, LocalArticle>

In my case I defined Repository class to have dao class in its constructor to call the function above from repository when creating Pager class.

My custom RemoteMediator class looks something like this below:

  • Note: In my case, there is no PREPEND case so RemoteMediator#load function always returns true when the value of the argument loadType is LoadType.PREPEND.
class FeedMediator(
    private val repository: FeedRepository
) : RemoteMediator<Int, LocalArticle>() {

    ... 

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, LocalArticle>
    ): MediatorResult = runCatching {
        when (loadType) {
            LoadType.PREPEND -> true
            LoadType.REFRESH -> {
                feedRepository.refresh()
                false
            }
            LoadType.APPEND -> {
                val continuation = feedRepository.continuation()
                if (continuation.isNullOrEmpty()) {
                    true
                } else {
                    loadFeedAndCheckContinuation(continuation)
                }
            }
        }
    }.fold(
        onSuccess = { endOfPaginationReached -> MediatorResult.Success(endOfPaginationReached) },
        onFailure = {
            Timber.e(it)
            MediatorResult.Error(it)
        }
    )

    private suspend fun loadFeedAndCheckContinuation(continuation: String?): Boolean {
        val feed = feedRepository.load(continuation)
        feedRepository.insert(feed)
        return feed.continuation.isNullOrEmpty()
    }

Finally you can create Pager class.

fun createFeedPager(
        mediator: FeedMediator<Int, LocalArticle>,
        repository: FeedRepository
    ) = Pager(
        config = PagingConfig(
            pageSize = FETCH_FEED_COUNT,
            enablePlaceholders = false,
            prefetchDistance = PREFETCH_DISTANCE
        ),
        remoteMediator = mediator,
        pagingSourceFactory = { repository.getUnreadPagingSource() }
    )

I hope it helps in some way..

Other references:


EDIT: After reading the doc again, I found a statement where the doc clearly states:

RemoteMediator to use for loading the data from the network into the local database.

Upvotes: -3

Related Questions