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

Kotlin Paging3调用多API资源:PagingSource与RemoteMediator选型及问题求解

PagingSource 与 RemoteMediator 适用场景说明

你的业务场景直接使用 PagingSource 即可,不需要用到RemoteMediator,二者适用场景区分如下:

  • PagingSource:适用于所有不需要本地数据库做缓存中间层的分页场景,不管是纯内存分页、纯网络分页,还是多接口拼接的分页逻辑,都可以直接在load方法中实现,你的两个网络接口没有持久化缓存需求,完全适配PagingSource的使用范围。
  • RemoteMediator:仅适用于「网络+本地数据库」双层架构的分页场景,核心作用是做网络数据和本地数据库的同步,当数据库数据不足时自动触发网络请求拉新,再将数据存入数据库,由数据库的PagingSource向UI层提供数据,适合需要离线可用、数据量较大需要持久化的场景。
现有代码类型错误修复

你遇到的类型不匹配报错是因为LoadResult.Page的data参数要求传入List类型,而你当前传入的是ArrayDeque类型,两种修复方案可选:

  1. 构造LoadResult.Page时直接做类型转换:
LoadResult.Page(
    data = data?.result?.toList() ?: emptyList(),
    prevKey = null,
    nextKey = data?.nextKey,
)
  1. 直接修改DataSource中MovieResult的result字段类型为List<Genre>,你当前场景不需要用到ArrayDeque的特性,直接使用List即可适配。
常见疑问解答

1. 列表顶部添加项的实现

如果需要在列表顶部新增数据,触发PagingSource.invalidate()即可触发全量刷新,调用逻辑如下:

  • 在ViewModel中持有MultiRequestPagingSource的实例,不要每次pagingSourceFactory都创建新实例
  • 需要新增顶部数据时,先将数据存入你维护的数据源中,再调用pagingSource.invalidate(),Paging会自动触发Refresh流程,加载最新的全量数据渲染到列表。如果是实现下拉刷新逻辑,直接调用PagingDataAdapter.refresh()即可自动触发刷新流程。

2. 获取第一个接口返回的id、name参数

可以通过StateFlow在ViewModel和UI层之间传递这两个参数,实现步骤如下:

  1. 修改DataSource中MovieResult结构,新增id、name字段存储第一个接口返回的对应值:
data class MovieResult(
    val result: List<Genre>?,
    val nextKey: String?,
    val movieId: Int?,
    val movieName: String?
)
  1. 在ViewModel中创建MutableStateFlow用来存储这两个参数:
val movieInfo = MutableStateFlow<Pair<Int?, String?>?>(null)
  1. 在PagingSource的load方法中,当触发Refresh请求(即首次加载/刷新)时,将拿到的id、name传入movieInfo
  2. 在Activity中监听movieInfo的变化,即可拿到对应参数用于UI展示。

3. 代码结构优化建议

现有结构整体逻辑是合理的,有几个可以优化的点:

  • 不要每次pagingSourceFactory都创建新的DataSource和PagingSource实例,在ViewModel中初始化一次即可,方便后续调用invalidate方法刷新数据
  • Api接口实例建议做成单例,不要每次请求都调用create()方法创建新实例,减少不必要的性能损耗
  • 异常捕获范围可以扩大,除了IOException外,还要捕获HttpException等其他网络相关异常
  • 给PagingConfig添加enablePlaceholders = false配置,避免列表出现空白占位项
  • 不需要Prepend逻辑的话可以直接返回null,无需额外处理

优化后的ViewModel示例:

class ActivityViewModel(app: Application) : AndroidViewModel(app) {
    private val dataSource = DataSource()
    private lateinit var pagingSource: MultiRequestPagingSource
    val movieInfo = MutableStateFlow<Pair<Int?, String?>?>(null)

    fun getMovie(): Flow<PagingData<Genre>> {
        return Pager(
            config = PagingConfig(
                pageSize = 20,
                enablePlaceholders = false
            ),
            pagingSourceFactory = {
                MultiRequestPagingSource(dataSource, movieInfo).also {
                    pagingSource = it
                }
            }
        ).flow
    }

    fun addItemToTop(genre: Genre) {
        dataSource.addTopItem(genre)
        pagingSource.invalidate()
    }
}

内容的提问来源于stack exchange,提问作者Compose Learner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 23:57:03