Kotlin Paging3调用多API资源:PagingSource与RemoteMediator选型及问题求解
PagingSource 与 RemoteMediator 适用场景说明
你的业务场景直接使用 PagingSource 即可,不需要用到RemoteMediator,二者适用场景区分如下:
- PagingSource:适用于所有不需要本地数据库做缓存中间层的分页场景,不管是纯内存分页、纯网络分页,还是多接口拼接的分页逻辑,都可以直接在
load方法中实现,你的两个网络接口没有持久化缓存需求,完全适配PagingSource的使用范围。 - RemoteMediator:仅适用于「网络+本地数据库」双层架构的分页场景,核心作用是做网络数据和本地数据库的同步,当数据库数据不足时自动触发网络请求拉新,再将数据存入数据库,由数据库的PagingSource向UI层提供数据,适合需要离线可用、数据量较大需要持久化的场景。
现有代码类型错误修复
你遇到的类型不匹配报错是因为LoadResult.Page的data参数要求传入List类型,而你当前传入的是ArrayDeque类型,两种修复方案可选:
- 构造
LoadResult.Page时直接做类型转换:
LoadResult.Page( data = data?.result?.toList() ?: emptyList(), prevKey = null, nextKey = data?.nextKey, )
- 直接修改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层之间传递这两个参数,实现步骤如下:
- 修改DataSource中
MovieResult结构,新增id、name字段存储第一个接口返回的对应值:
data class MovieResult( val result: List<Genre>?, val nextKey: String?, val movieId: Int?, val movieName: String? )
- 在ViewModel中创建
MutableStateFlow用来存储这两个参数:
val movieInfo = MutableStateFlow<Pair<Int?, String?>?>(null)
- 在PagingSource的
load方法中,当触发Refresh请求(即首次加载/刷新)时,将拿到的id、name传入movieInfo - 在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
相关产品推荐
相关产品推荐

