Skip to content

Append data after load in Remote Mediator #247

@Gizcerbes

Description

@Gizcerbes

I decided to try Pager without the Room database. I use HashMap as the simplest database.
First I implemented PagingSource.

class MapPagingSource(
    private val map: () -> Map<Int, List<Result>>
) : PagingSource<Int, Result>() {

    override fun getRefreshKey(state: PagingState<Int, Result>): Int? {
        println("TAG PagingSource anchorPosition  ${state.anchorPosition}")
        val anchor = state.anchorPosition ?: return null
        val closest = state.closestPageToPosition(anchor) ?: return null
        val page  = closest.prevKey?.plus(1) ?: closest.nextKey?.minus(1)
        return page
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Result> {
        val map = map()
        val position = (params.key ?: 0)
        println("TAG PagingSource position = $position, size = ${map.size}")
        return LoadResult.Page(
            data = map[position] ?: emptyList(),
            prevKey = if (position == 0) null else position - 1,
            nextKey = if (position + 1 < map.size) position + 1 else null
        )
    }
    
}

After RemoteMediator

@OptIn(ExperimentalPagingApi::class)
class PokemonApiRemoteMediator(
    private val map: () -> MutableMap<Int, List<Result>>,
    private val append: () -> Unit
) : RemoteMediator<Int, Result>() {

    private val ktor = Ktor()

    override suspend fun initialize(): InitializeAction {
        println("TAG RemoteMediator initialize")
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(loadType: LoadType, state: PagingState<Int, Result>): MediatorResult {
        val map = map()
        println("TAG RemoteMediator $loadType, ${map.size}")
        return try {
            val latestPage = when (loadType) {
                LoadType.REFRESH -> 0
                LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
                LoadType.APPEND -> map.size
            }
            val subList = runCatching {
                withContext(Dispatchers.IO) {
                    ktor.getData(state.config.pageSize, latestPage * state.config.pageSize)
                }
            }.getOrNull()

            val data =
                subList?.results ?: return MediatorResult.Success(endOfPaginationReached = true)
            if (data.isEmpty()) return MediatorResult.Success(endOfPaginationReached = true)

            if (loadType == LoadType.REFRESH) map.clear()
            map[latestPage] = data

            println("TAG RemoteMediator size ${map.size}")
            if (loadType == LoadType.APPEND) append()
            MediatorResult.Success(false)
        } catch (e: Throwable) {
            println("TAG RemoteMediator $e")
            MediatorResult.Error(e)
        }
    }
}

And created a Pager in ViewModel

    private val data: MutableMap<Int, List<Result>> = HashMap()
    private var latestSource: MapPagingSource? = null

    @OptIn(ExperimentalPagingApi::class)
    private val _pager = Pager(
        config = PagingConfig(10, enablePlaceholders = false),
        remoteMediator = PokemonApiRemoteMediator(map = { data }) { },
        pagingSourceFactory = {
            println("TAG create source")
            MapPagingSource { data }.also {
            latestSource = it
        } }
    val adapter = Adapter()

    init {
        _pager.flow
            .onEach {
                println("TAG PagingData $it")
                adapter.submitData(it)
            }.launchIn(viewModelScope)
    }

photo_2024-12-08_03-00-26

As I can see, the PagingData was created and sent to the adapter.
Then the PagingSource did not find the data and started the RemoteMediator.
After loading, the work stopped and the PagingSource did not start loading the data.
Of course, I can call "invalidate". But this will recreate the PagingSource, and will not add the data as if it were just making requests to the network from the PagingSource.
photo_2024-12-08_03-00-52

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions