android paging使用教程

以下是基于最新 Paging3 的 Android 分页库使用教程,结合官方文档和开发者实践总结:

一、基础配置

添加依赖

复制代码
// build.gradle  
dependencies {
    def paging_version = "3.2.1"
    implementation "androidx.paging:paging-runtime:$paging_version" 
    implementation "androidx.paging:paging-compose:$paging_version"  // Compose 支持 
    testImplementation "androidx.paging:paging-testing:$paging_version" 
}

核心组件

PagingSource:定义数据加载逻辑

Pager:配置分页参数并生成 PagingData 流

PagingDataAdapter:适配 RecyclerView 或 Compose 列表

RemoteMediator(可选):处理网络+本地缓存的分页协调

二、基础分页实现(纯网络请求)

创建 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) 
            LoadResult.Page(
                data = response.articles, 
                prevKey = if (page > 1) page - 1 else null,
                nextKey = if (response.hasMore)  page + 1 else null 
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}

ViewModel 中配置 Pager

复制代码
class ArticleViewModel : ViewModel() {
    private val api = RetrofitClient.apiService  
 
    val articles = Pager(
        config = PagingConfig(
            pageSize = 20,
            enablePlaceholders = false,
            initialLoadSize = 40 
        ),
        pagingSourceFactory = { ArticlePagingSource(api) }
    ).flow.cachedIn(viewModelScope) 
}

UI 层实现(RecyclerView)

// Adapter

复制代码
class ArticleAdapter : PagingDataAdapter<Article, ArticleViewHolder>(DIFF_CALLBACK) {
    companion object {
        val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Article>() {
            override fun areItemsTheSame(oldItem: Article, newItem: Article) = 
                oldItem.id  == newItem.id  
            override fun areContentsTheSame(oldItem: Article, newItem: Article) = 
                oldItem == newItem 
        }
    }
}

// Activity/Fragment

复制代码
lifecycleScope.launch  {
    viewModel.articles.collectLatest  { pagingData ->
        adapter.submitData(pagingData) 
    }
}

三、进阶场景(网络+数据库)

使用 RemoteMediator

复制代码
@ExperimentalPagingApi 
class ArticleRemoteMediator(
    private val db: AppDatabase,
    private val api: ApiService 
) : RemoteMediator<Int, Article>() {
 
    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, Article>
    ): MediatorResult {
        // 实现网络请求与数据库缓存的协调逻辑 
        // 详见官方示例:https://github.com/android/architecture-components-samples/tree/main/PagingWithNetworkSample  
    }
}

Room 集成

复制代码
@Dao 
interface ArticleDao {
    @Query("SELECT * FROM articles ORDER BY timestamp DESC")
    fun articlesPagingSource(): PagingSource<Int, Article>
}

// 创建 Pager 时组合 RemoteMediator

复制代码
Pager(
    config = PagingConfig(pageSize = 20),
    remoteMediator = ArticleRemoteMediator(db, api),
    pagingSourceFactory = { db.articleDao().articlesPagingSource()  }
)

四、状态监听与 UI 反馈

// 监听加载状态

复制代码
adapter.addLoadStateListener  { loadState ->
    when (loadState.refresh)  {
        is LoadState.Loading -> showLoading()
        is LoadState.NotLoading -> hideLoading()
        is LoadState.Error -> showError()
    }
}

// 错误重试

复制代码
binding.retryButton.setOnClickListener  {
    adapter.retry() 
}

五、最佳实践建议

性能优化

使用 cachedIn() 缓存数据流

合理设置 PagingConfig 参数(prefetchDistance 等)

实现高效的 DiffUtil 对比逻辑

测试策略

使用 TestPager 和 TestDispatcher 进行单元测试

验证边界条件(空列表、单页数据、加载错误等)

架构整合

配合 ViewModel + LiveData/Flow 使用

通过 Hilt/Dagger 实现依赖注入

结合 SwipeRefreshLayout 实现下拉刷新

完整实现示例可参考 Google 官方示例仓库 ,或通过 查看实际项目集成方式。

相关推荐
阿巴斯甜18 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker18 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952719 小时前
Andorid Google 登录接入文档
android
黄林晴20 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android