一、前言
Paging3,是Jetpack提供给开发者用来显示本地或者网络数据集的分页库。针对这类场景,传统的做法是用RecyclerView的加载更多来实现分页加载,很多逻辑需要自行处理且不一定完善。Paging3相当于是官网提供的一套解决方案。
下图为您应用的各个层级中推荐直接接入 Paging 的 Android 应用架构
二、添加依赖
根据语言二选一即可,我使用的是kotlin
//java
implementation 'androidx.paging:paging-runtime:3.0.0-alpha09'
//kotlin
implementation 'androidx.paging:paging-runtime-ktx:3.0.0-alpha09'
三、基本使用
基本使用主要包含如下内容:
- 配置数据源:PagingSource
- 构建分页数据:Pager、PagingData
- 构建RecyclerView Adapter:PagingDataAdapter
- 展示分页UI列表数据
- 设置Header和Footer
- 监听数据加载的状态
3.1 配置数据源
abstract class PagingSource<Key : Any, Value : Any>
参数解析:
- Key:分页标识类型,如页码,则为Int
- Value:返回列表元素的类型
3.1.1 Room
如果使用的是Room,从 2.3.0-alpha 开始,它将默认为您实现 PagingSource。在定义 Dao 接口的 Query 语句时,返回类型要使用 PagingSource
类型。同时不需要在 Query 里指定页数和每页展示数量,页数由 PagingSource
来控制,每页数量页在 PagingConfig
中定义。
@Dao
interface UserDao {
@Query("SELECT * FROM User ORDER BY name COLLATE NOCASE ASC")
fun allUserByName(): PagingSource<Int, User>
@Insert
fun insert(user: User)
@Delete
fun delete(user: User)
}
使用 Room 有一个好处是,如果通过 insert() 或 delete() 等方法修改了 Room 里的数据,不需要额外处理就会即时反应在 PagingSource 里,界面上展示的数据会相应变化。
3.1.2 自定义数据源
使用PagingSource
如果不是直接使用 Room 的数据,而是使用源自其他地方的数据,比如网络数据,就需要自定义 PagingSource 了,创建方式如下:
class UserDataSource : PagingSource<Int, User>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, User> {
return try {
val page = params.key ?: 0
//获取网络数据
val result = Retrofitance.instance.ApiService.getUsers(page)
LoadResult.Page(
//需要加载的数据
data = result.data,
//如果可以往上加载更多就设置该参数,否则不设置
prevKey = null,
//加载下一页的key 如果传null就说明到底了
nextKey = if (result.curPage == result.pageCount) null else page + 1
)
} catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
}
}
}
参数解释:
- data :返回的数据列表
- prevKey :上一页的key (传 null 表示没有上一页)
- nextKey :下一页的key (传 null 表示没有下一页)
- paging3 使用 flow 传递数据,不了解的可以搜索一下flow ;
- cachedIn:绑定协程生命周期,必须加上,否则可能崩溃;
- asLiveData:熟悉livedata的都知道怎么用;
使用RemoteMediator
RemoteMediator 和 PagingSource 相似,都需要覆盖 load() 方法,但是不同的是 RemoteMediator 不是加载分页数据到 RecyclerView 列表上,而是获取网络分页数据并更新到数据库中。
区别:
PagingSource
:实现单一数据源以及如何从该数据源中查找数据,例如 Room,数据源的变动会直接映射到 UI 上RemoteMediator
:实现加载网络分页数据并更新到数据库中,但是数据源的变动不能直接映射到 UI 上
在项目中如何进行选择?
PagingSource
:用于加载有限的数据集(本地数据库)例如手机通讯录等等。RemoteMediator
:主要用来加载网络分页数据并更新到数据库中,当我们没有更多的数据时,我们向网络请求更多的数据,结合 PagingSource 当保存更多数据时会直接映射到 UI 上
注意:
RemoteMediator 目前是实验性的 API ,所有实现 RemoteMediator 的类都需要添加 @OptIn(ExperimentalPagingApi::class)
注解。
当我们使用 OptIn 注解,需要在 App 模块下的 build.gradle 文件内添加以下代码
android {
kotlinOptions {
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
}
}
@OptIn(ExperimentalPagingApi::class)
class UserRemoteMediator(
val api: ApiService,
val db: AppDataBase
) : RemoteMediator<Int, User>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, User>
): MediatorResult {
try {
/**
* 在这个方法内将会做三件事
*
* 1. 参数 LoadType 有个三个值,关于这三个值如何进行判断
* LoadType.REFRESH
* LoadType.PREPEND
* LoadType.APPEND
*
* 2. 访问网络数据
*
* 3