android / android/codelab-android-paging

Append data after load in Remote Mediator

Open
#247 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Kotlin
Stars
509
Forks
268
PR merge metrics
No merged PRs in 30d

Description

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>
) : PagingSource() {

override fun getRefreshKey(state: PagingState): 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): LoadResult {
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>,
private val append: () -> Unit
) : RemoteMediator() {

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): 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> = 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](https://github.com/user-attachments/assets/d6053b2e-dd54-4a42-9a1e-1ced9bdb81c7)

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](https://github.com/user-attachments/assets/e80b1127-0387-46f7-8e26-bc19048ca438)

Contributor guide

Open the contributing guide

Research direction

Start by tracing the Pager flow through MapPagingSource.load and PokemonApiRemoteMediator.load, then compare the logged sequence with the PagingData submitted to the adapter. Check how the in-memory map changes during APPEND and what event should make the newly loaded data visible. Done means appended results appear in the adapter without relying on an unrelated workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.