Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

RemoteMediator PREPEND is getting called multiple times when not needed

未关闭
#205 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
25/100
Issue 类型
缺陷
描述清晰度
需要澄清
活跃度
停滞
技术栈
kotlin
领域
data

调研方向

从修改后的 PagingWithNetworkSample 开始,检查 PageKeyedRemoteMediator.load、postsOfSubreddit 中的 Pager 配置以及 RedditPostDao.postsBySubreddit。使用所述的 Paging、Room 和 Kotlin 版本重现 REFRESH、PREPEND 和 APPEND 序列,然后追踪边界加载重复的原因,并在更改行为之前定义预期的 request 数量。

由索引模型根据 Issue 内容生成。

描述

I'm facing an issue with paging library requesting 'too much' data when requesting the information from the middle of the 'data source':
For my scenario I can request both previous and next pages (Remote mediator load types PREPEND and APPEND). After initial portion (Remote mediator loadType REFRESH) and one APPEND and one PREPEND (as far as I can see from the source code paging lib will always request one page before and one page after after initial REFRESH) the library call mediator's load with 'PREPEND' loadType 1-3 more times, doesn't matter how big my page (and initial load) is, 30 or 300 elements. So my network calls sequence looks like

---> REFRESH
---> PREPEND
---> APPEND
---> PREPEND
---> PREPEND

When I'm scrolling and reaching boundary conditions, paging lib often requests 2-3 pages, instead of 1.

This all happens only when both PREVIOUS and NEXT pages are available. If user can scroll one direction only, most of the time, only 1 page is requested when user hits boundary condition.

Is it possible to control how many pages does paging library request (in order to reduce payload on the backend)

For the testing purposes, I modified this sample app to get 'endless stream of network data': https://github.com/android/architecture-components-samples/tree/master/PagingWithNetworkSample

@OptIn(ExperimentalPagingApi::class)
class PageKeyedRemoteMediator(
    private val db: RedditDb,
    private val redditApi: RedditApi,
    private val subredditName: String
) : RemoteMediator<Int, RedditPost>() {
    private val postDao: RedditPostDao = db.posts()
    private val remoteKeyDao: SubredditRemoteKeyDao = db.remoteKeys()

    var minIndex = 0
    var maxIndex = 0

    override suspend fun initialize(): InitializeAction {
        // Require that remote REFRESH is launched on initial load and succeeds before launching
        // remote PREPEND / APPEND.
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(
            loadType: LoadType,
            state: PagingState<Int, RedditPost>
    ): MediatorResult {
        try {
            delay(1000)
            val size = if (loadType == REFRESH) state.config.initialLoadSize else state.config.pageSize

            println(">>>>>    $loadType: ${if (loadType == APPEND) minIndex else maxIndex}")


            val items = (0 until size).map {
                val value = if (loadType == APPEND) minIndex-- else maxIndex++
                RedditPost(
                        name = value.toString(),
                        title = value.toString(),
                        score = value,
                        author = value.toString(),
                        subreddit = subredditName,
                        num_comments = value,
                        created = System.currentTimeMillis(),
                        thumbnail = null,
                        url = null
                ).apply {
                    indexInResponse = value
                }
            }

            db.withTransaction {
                if (loadType == REFRESH) {
                    postDao.deleteBySubreddit(subredditName)
                }

                postDao.insertAll(items)
            }

            return MediatorResult.Success(endOfPaginationReached = items.isEmpty())
        } catch (e: IOException) {
            return MediatorResult.Error(e)
        } catch (e: HttpException) {
            return MediatorResult.Error(e)
        }
    }
}
@Dao
interface RedditPostDao {
    ...
    @Query("SELECT * FROM posts WHERE subreddit = :subreddit ORDER BY num_comments DESC")
    fun postsBySubreddit(subreddit: String): PagingSource<Int, RedditPost>
    ...
}
    ...
    override fun postsOfSubreddit(subReddit: String, pageSize: Int) = Pager(
        config = PagingConfig(pageSize),
        remoteMediator = PageKeyedRemoteMediator(db, redditApi, subReddit)
    ) {
        db.posts().postsBySubreddit(subReddit)
    }.flow
    ...
    @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
    val posts = flowOf(
        clearListCh.receiveAsFlow().map { PagingData.empty<RedditPost>() },
        savedStateHandle.getLiveData<String>(KEY_SUBREDDIT)
            .asFlow()
            .flatMapLatest { repository.postsOfSubreddit(it, 100) }
            // cachedIn() shares the paging state across multiple consumers of posts,
            // e.g. different generations of UI across rotation config change
            .cachedIn(viewModelScope)
    ).flattenMerge(2)
    ...
    lifecycleScope.launchWhenCreated {
       model.posts.collectLatest {
           adapter.submitData(it)
        }
    }

Paging infra setup:

  1. Local db + Network (RemoteMediator)
  2. Room at persistence layer
  3. Coroutines
  4. Libs versions:
  • paging = "3.1.0-rc01"
  • room = "2.4.0-beta01"
  • kotlin = "1.4.21"
  • recyclerview = "1.2.1"
主要语言
Kotlin
星标
509
派生
268
PR 合并指标
30 天内没有已合并 PR

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

android/codelab-android-paging 的其他 Issue

查看 android/codelab-android-paging 的全部 Issue

相似的 Issue

更多 Kotlin Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。