一、核心概念与组件
Jetpack Paging 库旨在简化分页数据的加载和展示,特别适用于 RecyclerView 展示大量数据。Paging 3 是该库的最新版本,基于 Kotlin 协程和 Flow 构建,提供更简洁的 API 和更强的功能。
核心组件:
- PagingSource
负责加载分页数据,定义数据来源(如网络、数据库)。 - RemoteMediator
协调远程数据(网络)和本地数据(数据库)的分页加载,适用于混合数据源场景。 - PagingData
分页数据的容器,通过 Flow 或 LiveData 观察数据变化。 - PagingDataAdapter
RecyclerView 的适配器,自动处理分页数据的加载和更新。 - LoadState
表示加载状态(加载中、成功、失败),支持显示加载进度或错误提示。
二、使用示例
1. 基础配置
添加依赖:
gradle
// build.gradle (app)
dependencies {
implementation "androidx.paging:paging-runtime-ktx:3.2.1"
implementation "androidx.paging:paging-common-ktx:3.2.1"
// 可选:与 Room 集成
implementation "androidx.room:room-paging:2.6.1"
}
2. 定义数据模型
kotlin
data class Post(
val id: Int,
val title: String,
val body: String
)
3. 创建 PagingSource(网络数据源)
kotlin
class PostPagingSource(private val apiService: ApiService) : PagingSource<Int, Post>() {
override fun getRefreshKey(state: PagingState<Int, Post>): Int? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Post> {
return try {
val page = params.key ?: 1
val response = apiService.getPosts(page, params.loadSize)
LoadResult.Page(
data = response.posts,
prevKey = if (page > 1) page - 1 else null,
nextKey = if (response.hasNext) page + 1 else null
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
4. 在 ViewModel 中生成 PagingData
kotlin
class PostViewModel(private val apiService: ApiService) : ViewModel() {
val postsFlow = Pager(
config = PagingConfig(
pageSize = 20,
prefetchDistance = 5,
enablePlaceholders = false
),
pagingSourceFactory = { PostPagingSource(apiService) }
).flow.cachedIn(viewModelScope)
}
5. 创建 PagingDataAdapter
kotlin
class PostAdapter : PagingDataAdapter<Post, PostAdapter.ViewHolder>(POST_COMPARATOR) {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(post: Post) {
itemView.findViewById<TextView>(R.id.title).text = post.title
itemView.findViewById<TextView>(R.id.body).text = post.body
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_post, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = getItem(position)
post?.let { holder.bind(it) }
}
companion object {
private val POST_COMPARATOR = object : DiffUtil.ItemCallback<Post>() {
override fun areItemsTheSame(oldItem: Post, newItem: Post) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Post, newItem: Post) =
oldItem == newItem
}
}
}
6. 在 Activity/Fragment 中绑定数据
kotlin
class PostListFragment : Fragment() {
private lateinit var adapter: PostAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(requireContext())
adapter = PostAdapter()
recyclerView.adapter = adapter
// 观察数据
viewLifecycleOwner.lifecycleScope.launch {
viewModel.postsFlow.collectLatest { pagingData ->
adapter.submitData(pagingData)
}
}
}
}
三、适用场景
- 无限滚动列表
在社交应用、新闻应用中加载动态或文章。 - 本地数据库缓存
使用 Room 作为数据源,结合网络分页更新数据。 - 多数据源混合
例如先展示本地缓存数据,再加载网络最新数据(需使用RemoteMediator
)。 - 实时数据更新
当数据源变化时(如数据库更新),自动刷新列表。
四、注意事项
1. 配置参数优化
pageSize
:每页加载的数据量,需与后端一致。prefetchDistance
:提前加载下一页的阈值,建议为pageSize
的 1-2 倍。enablePlaceholders
:是否显示占位符,通常设为false
以提高性能。
2. 内存管理
- 避免在
PagingSource
中持有 Context 或 View 引用,防止内存泄漏。
3. 错误处理与重试
-
监听
LoadState
显示错误界面,并提供重试按钮:kotlinadapter.addLoadStateListener { loadState -> if (loadState.refresh is LoadState.Error) { showErrorView() } }
4. 与 Room 集成
-
使用
RemoteMediator
协调网络和数据库:kotlin@ExperimentalPagingApi class PostRemoteMediator( private val db: AppDatabase, private val apiService: ApiService ) : RemoteMediator<Int, Post>() { ... }
五、版本兼容性
Paging 版本 | 最低 Android API | 主要特性 |
---|---|---|
3.0+ | API 21 (Android 5.0) | 基于 Kotlin Flow 和协程,支持 RxJava 3、错误处理改进。 |
2.x | API 14 (Android 4.0) | 基于 LiveData 和 RxJava 2,功能较旧。 |
- 兼容性建议 :
- 新项目直接使用 Paging 3。
- 旧项目迁移时,注意替换
LivePagedListBuilder
为Pager
,并适配数据源逻辑。
六、总结
- 优势:简化分页逻辑、支持复杂数据源、自动管理生命周期。
- 最佳实践 :
- 使用
DiffUtil
提升列表更新效率。 - 结合
RemoteMediator
实现离线优先架构。 - 监控
LoadState
优化用户体验。
- 使用
通过合理配置和优化,Jetpack Paging 能显著提升列表类功能的性能和可维护性。