安卓Paging3下如何解决RecyclerView循环显示重复数据问题
问题复现
当前接入Paging3实现产品列表加载,配套使用LiveData监听数据变更、在适配器中配置DiffUtil比较器,实际接口无分页大小参数、无产品唯一ID。异常表现为:接口固定返回10条产品数据,列表展示完全量后,用户滑到列表底部会重复加载完全相同的10条数据,无限循环没有终止。
问题根因
三个潜在实现问题全部会导致上述异常:
- 分页next/prev key逻辑完全匹配错误场景。当前实现只要接口返回的产品列表非空,就将nextKey设置为当前页码+1,给Paging框架传递“还有下一页数据”的信号;但接口本身不支持按页码拉取,每次调用都会返回固定的10条全量数据,框架会持续触发下一页加载,形成无终止的重复加载循环。
- DiffUtil比较器的唯一性判断存在漏洞。仅用产品名称判断是否为同一个item,遇到同名产品会出现item匹配错误,在重复加载相同数据时无法正确识别内容无变化,会触发无意义的列表刷新。
getRefreshKey的实现逻辑不合理。直接返回state.anchorPosition会导致下拉刷新时,Paging框架从当前可视位置开始拉取数据,结合错误的分页key逻辑,会进一步放大重复加载的问题。
修复方案
1. 调整ProductPagingSource的分页逻辑,适配无后端分页的实际场景
因为接口不支持分页拉取,所有数据为单次全量返回,无需模拟页码递增的分页流程:
class ProductPagingSource( private val productApi: ProductApi, ) : PagingSource<Int, Products>() { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Products> { return try { // 全量数据单次拉取,不依赖页码参数 val response = productApi.getProducts() val products = response.products LoadResult.Page( data = products, // 全量数据无前后分页,两个key直接置空,告知Paging框架当前批次为全部数据 prevKey = null, nextKey = null ) } catch (exception: IOException) { LoadResult.Error(exception) } catch (exception: HttpException) { LoadResult.Error(exception) } } override fun getRefreshKey(state: PagingState<Int, Products>): Int? { // 全量数据场景下,刷新时从头加载即可,直接返回null return null } }
逻辑说明:只要nextKey置为null,Paging框架在加载完当前返回的10条数据后,就会判定没有更多数据,不会触发后续重复的网络请求。
2. 优化适配器DiffUtil比较器的判断逻辑
针对接口无唯一ID的情况,避免仅用单一字段判断item唯一性的漏洞:
private val PRODUCT_COMPARATOR = object : DiffUtil.ItemCallback<Products>() { // 用组合固有属性判断是否为同一个产品,降低同名单品的误判概率 override fun areItemsTheSame(oldItem: Products, newItem: Products) = oldItem.name == newItem.name && oldItem.sku == newItem.sku // 全量字段对比判断内容是否一致,无变化时跳过无意义的视图刷新 override fun areContentsTheSame(oldItem: Products, newItem: Products) = oldItem == newItem }
3. 补充Paging配置适配
匹配全量一次加载的场景,调整PagingConfig参数,避免框架预加载触发多余请求:
val pagingConfig = PagingConfig( pageSize = 10, // 与接口返回的总数据量保持一致 prefetchDistance = 20, // 预取距离大于总数据量,列表滑到底前不会触发额外加载 enablePlaceholders = false )
内容的提问来源于stack exchange,提问作者user14016240
相关产品推荐
相关产品推荐

