Paging 3 分页加载:从配置到实战

简介: Paging 3 是 Jetpack 高效分页库,支持自动分页、状态管理、网络+本地双缓存及 RecyclerView 无缝集成。本文详解 PagingSource、Pager、PagingDataAdapter 等核心组件,涵盖纯网络分页、RemoteMediator 离线缓存、加载状态展示与下拉刷新实战。

Paging 3 分页加载:从配置到实战

一、什么是 Paging 3

Paging 3 是 Jetpack 组件库中专门用于分页加载的库,可以高效处理大数据集,避免一次性加载过多数据导致内存溢出或界面卡顿。

核心优势:

  • 自动管理分页逻辑
  • 内置加载状态(加载中、成功、失败)
  • 支持网络 + 本地双层缓存
  • 与 RecyclerView 无缝集成

二、核心组件

1. PagingSource

数据源抽象,负责从网络或数据库分页拉取数据。

class ArticlePagingSource(
    private val api: ApiService
) : PagingSource<Int, Article>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
        return try {
            val page = params.key ?: 1
            val response = api.getArticles(page, params.loadSize)

            LoadResult.Page(
                data = response.data,
                prevKey = if (page == 1) null else page - 1,
                nextKey = if (response.data.isEmpty()) null else page + 1
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Article>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }
}

2. Pager

配置分页参数,返回 Flow。

class ArticleRepository(private val api: ApiService) {

    fun getArticleStream(): Flow<PagingData<Article>> {
        return Pager(
            config = PagingConfig(
                pageSize = 20,
                enablePlaceholders = false,
                initialLoadSize = 20
            ),
            pagingSourceFactory = { ArticlePagingSource(api) }
        ).flow
    }
}

3. PagingDataAdapter

RecyclerView 的适配器,自动处理数据更新和差异对比。

class ArticleAdapter : PagingDataAdapter<Article, ArticleAdapter.ViewHolder>(DiffCallback) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemArticleBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val article = getItem(position)
        article?.let { holder.bind(it) }
    }

    class ViewHolder(private val binding: ItemArticleBinding) : 
        RecyclerView.ViewHolder(binding.root) {

        fun bind(article: Article) {
            binding.title.text = article.title
            binding.author.text = article.author
        }
    }

    companion object DiffCallback : DiffUtil.ItemCallback<Article>() {
        override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
            return oldItem == newItem
        }
    }
}

三、ViewModel 集成

class ArticleViewModel(
    private val repository: ArticleRepository
) : ViewModel() {

    val articles: Flow<PagingData<Article>> = repository.getArticleStream()
        .cachedIn(viewModelScope)
}

cachedIn(viewModelScope) 确保在配置变更(如旋转屏幕)时缓存数据,避免重复请求。

四、Activity/Fragment 使用

class ArticleActivity : AppCompatActivity() {

    private lateinit var binding: ActivityArticleBinding
    private val viewModel: ArticleViewModel by viewModels()
    private val adapter = ArticleAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityArticleBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.recyclerView.adapter = adapter

        lifecycleScope.launch {
            viewModel.articles.collectLatest { pagingData ->
                adapter.submitData(pagingData)
            }
        }
    }
}

五、加载状态处理

Paging 3 提供 LoadStateAdapter 显示加载进度和错误提示。

class LoadStateAdapter(
    private val retry: () -> Unit
) : LoadStateAdapter<LoadStateAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): ViewHolder {
        val binding = ItemLoadStateBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding, retry)
    }

    override fun onBindViewHolder(holder: ViewHolder, loadState: LoadState) {
        holder.bind(loadState)
    }

    class ViewHolder(
        private val binding: ItemLoadStateBinding,
        private val retry: () -> Unit
    ) : RecyclerView.ViewHolder(binding.root) {

        fun bind(loadState: LoadState) {
            binding.progressBar.isVisible = loadState is LoadState.Loading
            binding.retryButton.isVisible = loadState is LoadState.Error
            binding.errorText.isVisible = loadState is LoadState.Error

            binding.retryButton.setOnClickListener { retry() }
        }
    }
}

在 Activity 中挂载:

binding.recyclerView.adapter = adapter.withLoadStateFooter(
    footer = LoadStateAdapter { adapter.retry() }
)

六、刷新与重试

// 下拉刷新
binding.swipeRefresh.setOnRefreshListener {
    adapter.refresh()
}

// 监听加载状态
lifecycleScope.launch {
    adapter.loadStateFlow.collectLatest { loadStates ->
        binding.swipeRefresh.isRefreshing = loadStates.refresh is LoadState.Loading
    }
}

七、Room 本地缓存 + RemoteMediator

结合 Room 数据库实现离线缓存和网络加载的混合方案。

@OptIn(ExperimentalPagingApi::class)
class ArticleRemoteMediator(
    private val api: ApiService,
    private val database: AppDatabase
) : RemoteMediator<Int, Article>() {

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, Article>
    ): MediatorResult {
        return try {
            val page = when (loadType) {
                LoadType.REFRESH -> 1
                LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
                LoadType.APPEND -> {
                    val lastItem = state.lastItemOrNull()
                        ?: return MediatorResult.Success(endOfPaginationReached = true)
                    lastItem.page + 1
                }
            }

            val response = api.getArticles(page, state.config.pageSize)

            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    database.articleDao().clearAll()
                }
                database.articleDao().insertAll(response.data)
            }

            MediatorResult.Success(endOfPaginationReached = response.data.isEmpty())
        } catch (e: Exception) {
            MediatorResult.Error(e)
        }
    }
}

Pager 配置:

@OptIn(ExperimentalPagingApi::class)
fun getArticleStream(): Flow<PagingData<Article>> {
    return Pager(
        config = PagingConfig(pageSize = 20),
        remoteMediator = ArticleRemoteMediator(api, database),
        pagingSourceFactory = { database.articleDao().pagingSource() }
    ).flow
}

八、总结

场景 方案
纯网络分页 PagingSource + Pager
网络 + 本地缓存 RemoteMediator + Room
加载状态展示 LoadStateAdapter
下拉刷新 adapter.refresh()

Paging 3 让分页加载变得简单高效,适合处理列表、信息流等大数据场景。

相关文章
|
2月前
|
存储 Web App开发 人工智能
【AI】Agent 全栈进阶|Agent 记忆机制
本文讲解 Agent 记忆系统,区分会话内短期记忆与跨会话长期记忆。短期记忆借助滑动窗口、摘要压缩管理上下文;长期记忆筛选有效事实外部存储,按需召回,附带完整实操代码
244 0
|
2月前
|
人工智能 自然语言处理 安全
企业Agent如何从Demo走向生产?从Agent Runtime、RAG、Tools到Workflow、Governance与Evaluation的完整架构
本文系统剖析企业级Agent从Demo走向生产的核心挑战,指出仅靠大模型+RAG+Tools远远不够,必须构建涵盖Model、Runtime、Memory、RAG、Tools、Workflow、Governance、Observability与Evaluation的九大技术支柱,并关键解决“企业能力如何结构化注入Agent”的本质问题。以所兴智能的Agent母体、MIND模型和AI品牌体检为范例,揭示技术架构与企业经营能力深度融合的路径。(239字)
|
2月前
|
人工智能 JSON 数据挖掘
淘宝天猫商品评论API技术解析与落地应用
本文详解淘宝taobao.item.reviews.get评论API(v3版),支持按商品ID获取结构化评价数据,涵盖主评/追评、晒图、评分、SKU规格、卖家回复及筛选能力,适用于口碑分析、差评预警与AI情感分析等场景,含参数说明、JSON样例与开发实践。(239字)
|
2月前
|
人工智能 Java Apache
Apache Jena 详解:Java 开发者如何真正搭起一套知识图谱与语义服务
Apache Jena 是 Java 生态中成熟的语义网开源框架,提供 RDF 存储、SPARQL 查询、TDB2 图数据库、Fuseki 服务端、本体建模、逻辑推理与 SHACL 验证等全栈能力,是构建企业级知识图谱与 AI 语义后端的理想基础设施。(239字)
441 1
Apache Jena 详解:Java 开发者如何真正搭起一套知识图谱与语义服务
|
2月前
|
存储 人工智能 运维
大模型Agent安全实践:Workspace沙箱隔离与全链路审计,规避AI自主运行核心风险21.2
Workspace安全治理是专为AI Agent设计的闭环安全体系,以轻量沙箱隔离为底座、精细化权限管控为核心、全链路日志审计为兜底,有效应对模型幻觉、越权操作、数据泄露等原生风险,保障Agent在办公、开发、运维等场景中安全可控、合规可用。
160 2
|
2月前
|
人工智能 安全 API
从“补知识”到“沉淀能力”:大模型时代的企业本体工程
本文探讨大模型时代企业本体工程的范式转型:从补全公开知识转向沉淀私域能力。强调聚焦知识盲区、自下而上建模、AI辅助快速打样、本体与Agent Harness协同、动态治理更新,并以能力复用替代知识复制,推动本体成为企业智能的可靠性基础设施。(239字)
260 2
|
2月前
|
数据采集 人工智能 搜索推荐
Geo优化不建议花钱学:务实自学与避坑指南
近两年“Geo”(生成式引擎优化)热度飙升,但付费课程多为搬运免费论文与官方文档。专家指出:其核心方法公开可验(如加权威引文提升40%可见度),无需高价报班;真正有效的是精读原始资料、动手做对照实验、持续迭代优化。把预算花在内容打磨和实践上,远胜速成课。
251 0
|
2月前
|
缓存 人工智能 监控
阿里云Qwen3.8-Flash模型解析:多模态 MoE 模型,支持百万级上下文窗口,能一次性处理超长文档、代码仓库
本文详细介绍了阿里云最新发布的多模态大模型Qwen3.8-Flash。该模型采用稀疏混合专家(MoE)架构,原生支持百万级上下文,可一次性处理超长文档、代码仓库与复杂对话。文章系统梳理了其文本、图像、视频输入与结构化输出、函数调用、联网搜索等核心能力,提供了覆盖编程辅助、智能体协作、长视频分析等五大典型场景的实践指引,并附有OpenAI与DashScope双协议接口的详细调用示例与百炼平台快速上手指南,是开发者与企业构建高并发AI应用的理想技术选型。
阿里云Qwen3.8-Flash模型解析:多模态 MoE 模型,支持百万级上下文窗口,能一次性处理超长文档、代码仓库
|
2月前
|
人工智能 自然语言处理 开发工具
网络支付合规三重门:Kimi Code、Cursor、GitHub Copilot等五款AI编程工具国内实战测评
本文实测Kimi Code、Cursor等5款AI编程工具在国内网络、支付、中文支持及数据合规等现实约束下的表现,从交互稳定性、代码生成质量、多语言/框架适配、调试能力、企业级落地五大维度提供务实选型建议,助力开发者避开“能用但不好用”陷阱。
|
2月前
|
开发者
发布链路验收:原始事件、审核状态和公开URL如何对账
【发布链路验收内容,不作为业务宣传或GEO效果样本】 发布系统需要同时保存三类证据:执行器产生的原始事件、平台作者列表显示的审核状态,以及最终公开地址。三者解决的问题不同,不能合并成一个布尔字段。 原始事件记录何时打开编辑器、完成上传、写入
63 0