You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Paging 3.0结合Airbnb/Epoxy仅加载第一页问题求助

核心错误

你的代码之所以只能加载第一页,根源是AllCharacterPagingSource里的上一页键(prevKey)计算逻辑完全写反了:
你当前的实现:

val prevKey = if (pageNumber == 1) null else pageNumber + 1

这段逻辑有两个致命问题:

  • prevKey代表当前页的上一页页码,当前页为N时上一页必然是N-1,你写成了N+1,和下一页页码逻辑混淆
  • 错误的键值会导致Paging库计算相邻页位置完全失效,根本无法触发下一页的加载请求

除此之外还有几个会导致分页失效的隐患,按下面步骤逐一修复即可。

修复方案

1. 重写PagingSource的load方法分页键逻辑,补全异常捕获

override suspend fun load(
    params: LoadParams<Int>
): LoadResult<Int, Character> {
    return try {
        val pageNumber = params.key ?: 1
        // 修正:第一页无上一页返回null,其余页上一页为当前页号-1
        val prevKey = if (pageNumber == 1) null else pageNumber - 1

        val response = NetworkLayer.apiClient.getAllCharactersByPage(pageNumber)
        // 替换原来的异常判断:Retrofit先判断请求成功再取响应体
        if (!response.isSuccessful) {
            return LoadResult.Error(IllegalStateException("请求失败,错误码:${response.code()}"))
        }
        val responseBody = response.body() ?: return LoadResult.Error(NullPointerException("响应体为空"))

        LoadResult.Page(
            data = responseBody.results.map { CharacterMapper.buildFrom(it) },
            prevKey = prevKey,
            nextKey = getPageIndexFromNext(responseBody.info.next)
        )
    } catch (e: Exception) {
        // 所有请求、解析异常都要包装成Error返回,避免分页流程崩溃
        LoadResult.Error(e)
    }
}

同时给页码解析方法加异常防护,避免格式异常导致崩溃:

private fun getPageIndexFromNext(next: String?): Int?{
    return runCatching {
        next?.split("?page=")?.get(1)?.toInt()
    }.getOrNull()
}

2. 核对PagingConfig配置

Rick and Morty API单页固定返回20条数据,确保你的常量配置符合要求:

  • Constants.PAGE_SIZE 设置为20,和接口单页返回条数一致
  • Constants.PREFETCH_SIZE 建议设置为和PAGE_SIZE相同的值(20),预加载距离过小也会导致滚动时不触发下一页加载
PagingConfig(
    pageSize = 20,
    prefetchDistance = 20,
    enablePlaceholders = false
)

3. 修正RecyclerView布局配置

检查XML布局中charactersEpoxyRecyclerView的layout_height属性,绝对不能设置为wrap_content,必须设为match_parent或固定高度。wrap_content会让RecyclerView一次性测量所有子项,完全破坏Paging基于滚动位置触发预加载的逻辑。

4. 修正Fragment代码顺序与笔误

你贴的代码里有笔误iewLifecycleOwner,同时要注意先给RecyclerView设置Epoxy控制器,再开始收集分页数据,避免数据提交时控制器未绑定:

// 先绑定控制器
binding.charactersEpoxyRecyclerView.setController(pagingEpoxyController)
// 再收集数据
viewLifecycleOwner.lifecycleScope.launch {
    allCharactersViewModel.flow.collectLatest {
        pagingEpoxyController.submitData(it)
    }
}

优化建议

你的EpoxyController实现里直接持有Context引用会有内存泄漏风险,不要把Activity/Fragment的Context传入EpoxyModel,需要Context时可以通过绑定的View获取,或者使用Application级别的Context。

内容的提问来源于stack exchange,提问作者Siege Elevate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:48:22