使用 Paging3 实现按需加载功能
一、概述
1.1 前言
- 在上一篇文章使用 Paging2 实现按需加载功能中,介绍了如何使用 paging2 + Room 从本地获取数据,今天就是用最新的 Paging3 来进行一个网络数据的分页加载。
- Demo 使用到的技术包含 Paging3、Retrofit、Coroutines、Flow 等,有不熟悉的同学可以先去了解一下,这里不再对其他几个库做详细说明。
1.2 与 Paging2 的区别
- Paging3 对比 Paging2 几乎可以称为完全不兼容的跨代升级,前面说过 Paging2 有几个非常重要的类:
PageListAdapter
、PagedList
、DataSource
,分别用于绑定和展示分页数据
、持有分页数据
以及提供分页数据
。而在 Paging3 中,变成了下面三者:PagingDataAdapter
:取代了 PagedList,支持增量更新,可以根据新加载的数据自动更新列表,并提供了更好的性能和动画效果。PagingData
:取代了 PagedList,是一个流(Flow)类型,与 PagedList 不同,PagingData 可以直接与 Kotlin 流 API 进行集成,在数据变化时提供更强大的操作和转换功能。PagingSource
:取代了 DataSource,用于定义如何从数据源加载分页数据,且不再区分 ItemKeyedDataSource、PageKeyedDataSource 或 PositionalDataSource,而是提供了一种通用的方式来加载数据。这也是为什么上篇文章我没有使用 Paging2 来加载网络中的数据,毕竟已经过时了嘛,所以认识一下就好不需要深入了解。
二、使用
- 这次的 Demo 是使用 Github 官方提供的 api 获取 Android 相关的开源库,按 Star 由多到少排名,且加载为分页加载。
- Demo 是参考郭霖大神的文章Jetpack新成员,Paging3从吐槽到真香后自己进行实践搭建的。
2.1 引入依赖
1 |
|
2.2 结构
2.3 编码
2.3.1 BaseResp
- 基本的响应类,用于包装从服务器端获取的开源库数据
1 | class BaseResp { |
2.3.2 GitRepo
- 每一个开源库数据对应的实体类,为了简单易读,去掉了很多不需要的字段
1 | data class GitRepo( |
2.3.3 Retrofit
- 网络请求相关的工具类和接口配置
1 | object RetrofitClient { |
2.3.4 GitRepoPagingSource
- 负责从数据源加载分页数据,继承自 PagingSource<Key, T>,实现 load() 方法,该方法根据传入的 LoadParams 参数来加载分页数据,并返回
LoadResult<Key, T>
对象。LoadResult 是一个包含加载结果和状态的 sealed class,它可以是 LoadResult.Page(成功加载一页数据)、LoadResult.Error(加载错误)或 LoadResult.EndOfPagination(已加载完所有数据)。 LoadParams
包含了请求的页数、加载大小以及初始加载或刷新操作的信息,根据 LoadParams 提供的信息,可以方便地进行网络请求,获取相应页码的数据,并根据加载情况返回对应的 LoadResult。
1 | class GitRepoPagingSource(private val servicesConfig: ServicesConfig) : |
2.3.5 adapter
- GitRepoAdapter 负责加载和展示分页数据,并通过监听滚动事件和 LoadState 实现自动加载更多的功能。
- GitRepoFooterAdapter 即底部的加载提示,可以随意自定义样式。
1 | class GitRepoAdapter : PagingDataAdapter<GitRepo, GitRepoAdapter.MyViewHolder>(diffCallback) { |
2.3.6 GitRepository
- 值得注意的类有两个:
Flow
:Kotlin 中提供的协程流(Flow)API,可以用来处理异步数据流。Pager
:用于创建分页数据源的工具类,接收两个参数,config:一个 PagingConfig 对象,表示分页配置信息,如每页加载数量等;pagingSourceFactory:一个函数,用于创建分页数据源。Pager 还实例提供了一个 flow 属性,它返回一个 Flow<PagingData> 对象,表示一个包含分页数据的异步数据流。
- Flow 的话,它是 Kotlin 协程库中提供的一种用于处理异步数据流的工具,可以用于在异步场景下处理连续的、可能无限的数据集合。简单来说,Flow 可以看作是一种类似于集合的数据结构,但不同于集合的是,它不会一次性返回所有的数据,而是以异步的方式逐个或批量地
发射
数据,我们只需要在对应的地方接收(collect)即可。 - 最后,通过 getGitRepoPagingData() 方法,返回一个 Flow<PagingData
> 对象。
1 | class GitRepository { |
2.3.7 GitRepoViewModel
- 连接界面和数据的中间层,负责处理业务逻辑和分页数据的管理
1 | class GitRepoViewModel : ViewModel() { |
2.3.8 Paging3Activity
- 解释几个点:
addLoadStateListener
:PagingDataAdapter 提供的函数,用于监听加载状态的变化,我们可以在这里判断数据的获取状态,进而进行 UI 方面的更新提示操作。refresh()
:也是 PagingDataAdapter 提供的函数,用于刷新数据。withLoadStateFooter()
:PagingDataAdapter 的一个扩展函数,用于为适配器添加加载状态的底部视图,这里我们将自己定义的 GitRepoFooterAdapter 放进去。collect
:前面提到 Flow 的时候说过接收数据怎么接收,就是使用这里的collect
函数,实现从数据流中收集并处理分页数据。还要注意 collect 是一个挂起函数
,它会暂停当前协程,并等待数据流的发射,所以在调用 collect 之前,需要确保代码运行在协程作用域内,比如这里就使用了lifecycleScope.launch { ... }
来开启一个协程。
1 | class Paging3Activity : AppCompatActivity() { |
四、总结
- 这里就不再做什么总结了,相关的内容都在代码中有所解释。
- 再看一下整个工作流程:
- 另外,上面没有提到本地数据的加载,其实非常简单,因为不需要自己编写 PagingSource 了,我们可以直接在 Dao 中定义获取数据的方法返回类型为
PagingData<Int, T>
,后续操作就和上面获取到网络数据一样了。说白了,数据无论从哪儿来,只要获取到了,就被视为数据源,后续都做同样的处理。 - 个人的使用感受还是挺不错的,对于官方所说的功能强大且易于使用,我觉得功能强大确实是强大,易于使用的话只能说还得多练才行呐!而且还有一些高级的用法并没有提及到,比如 RemoteMediator、getRefreshKey()等,这些等到后面体验了之后再来分享。
- 有任何问题都可以留言告诉我,感谢!