Jetpack 新成员 Paging3 网络实践及原理分析(二)
- 如果评论区没有及时回复,欢迎来公众号:ByteCode 咨询
- 公众号:ByteCode。致力于分享最新技术原创文章,涉及 Kotlin、Jetpack、算法、译文、系统源码相关的文章
前言
Google 最近更新了几个 Jetpack 新成员 Hilt、Paging 3、App Startup 等等。
在之前的文章里面分别分析 App Startup 实践以及原理 和 Paging3 加载本地数据(一)实践以及原理,如果没有看过可以点击下方地址前去查看:
- Jetpack 最新成员 AndroidX App Startup 实践以及原理分析
- Jetpack 成员 Paging3 数据实践以及源码分析(一)
- Jetpack 成员 Paging3 网络实践及原理分析(二)
- Jetpack 成员 Paging3 使用 RemoteMediator 实现加载网络分页数据并更新到数据库中(三)
- 代码地址:https://github.com/hi-dhl/AndroidX-Jetpack-Practice
今天这边文章主要来分析 Paging3 加载网络数据及其原理,利用周末的时间参考 Google 文档实现了 Paging3 期间也遇到一些坑,会在文中详细分析,代码已经上传到了 GitHub:Paging3SimpleWithNetWork
通过这篇文章你将学习到以下内容:
- Paging3 是什么?
- Paging3 相对之前版本 (Paging1、Paging2) 核心的变化?
- 关于 Paging 支持的分页策略?
- 在项目中如何使用 Paging3 去加载网络数据?
- Paging3 网络异常如何处理?
- Paging3 如何监听网络请求状态?
- Paging3 如何进行刷新和重试?
在项目 Paging3SimpleWithNetWork 中用到了 Coil(Kotlin 图片加载库)、Databinding(数据绑定)、Anko(主要用来替换替代 XML 使用的方式)、Koin(Kotlin 依赖注入库)、JDatabinding(基于 Databinding 封装的组件)、Data Mapper(数据映射)、使用 Composing builds 作为依赖库的版本管理、Repository 设计模式、MVVM 架构等等,关于这里一些技术之前没有了解过,可以点击下面连接前往查看。
- [译][1.4K+ Star] Kotlin 新秀 Coil VS Glide and Picasso
- [译][2.4K Star] 放弃 Dagger 拥抱 Koin
- 再见吧 buildSrc, 拥抱 Composing builds 提升 Android 编译速度
- 项目中封装 Kotlin + Android Databinding
- 为数不多的人知道的 Kotlin 技巧以及 原理解析
Paging3 是什么?
Paging 是一个分页库,它可以帮助您从本地存储或通过网络加载显示数据。这种方法使你的 App 更有效地使用网络带宽和系统资源。
Google 推荐使用 Paging 作为 App 架构的一部分,它可以很方便的和 Jetpack 组件集成,Paging3 包含了以下功能:
- 在内存中缓存分页数据,确保您的 App 在使用分页数据时有效地使用系统资源。
- 内置删除重复数据的请求,确保您的 App 有效地使用网络带宽和系统资源。
- 可配置 RecyclerView 的 adapters,当用户滚动到加载数据的末尾时自动请求数据。
- 支持 Kotlin 协程和 Flow, 以及 LiveData 和 RxJava。
- 内置的错误处理支持,包括刷新和重试等功能。
Paging3 相对于之前类的职能变化
在 Paging3 之前提供了 ItemKeyedDataSource、PageKeyedDataSource、PositionalDataSource 这三个类,在这三个类中进行数据获取的操作。
- PositionalDataSource:主要用于加载数据有限的数据(加载本地数据库)
- ItemKeyedDataSource:主要用来请求网络数据,它适用于通过当前页面最后一条数据的 id,作为下一页的数据的开始的位置,例如 Github 的 API。
- 例如地址
https://api.github.com/users?since=0?per_page=30
当 since = 0 时获取第一页数据,当前页面最后一条数据的 ID 是 46。 - 将 46 作为开始位置,此时 since = 46,地址变成:
https://api.github.com/users?since=46?per_page=30
。
- 例如地址
- PageKeyedDataSource:也是用来请求网络数据,它适用于通过页码分页来请求数据。
在 Paging3 之后 ItemKeyedDataSource、PageKeyedDataSource、PositionalDataSource 合并为一个 PagingSource,所有旧 API 加载方法被合并到 PagingSource 中的单个 load() 方法中。
abstract suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value> |
这是一个挂起函数,实现这个方法来触发异步加载,具体实现见下文,另外在 Paging3 中还有以下变化
- LivePagedListBuilder 和 RxPagedListBuilder 合并为了 Pager。
- 使用 PagedList.Config 替换 PagingConfig。
- 使用 RemoteMediator 替换了 PagedList.BoundaryCallback 去加载网络和本地数据库的数据。
四步实现 Paging3 加载网络数据
Google 推荐我们使用 Paging3 时,在应用程序的三层中操作,以及它们如何协同工作加载和显示分页数据,如下图所示:
我们接下来按照 Google 推荐的方式开始实现,只需要四步即可实现 Paging3 加载网络数据,文中只贴出核心代码,具体实现可以看 GitHub 上的 Paging3SimpleWithNetWork 项目,先在 App 模块中的 build.gradle 文件中添加以下代码:
dependencies { |
1. 网络请求部分
这里选择使用的是 GitHub API
interface GitHubService { |
注意: 这里需要在 getGithubAccount 方法前添加 suspend 关键字,否则调用的时候,会抛出以下异常。
Unable to create call adapter for XXXXX |
2. 在 Repository 层创建 PagingSource 数据源
class GitHubItemPagingSource( |
PagingSource 是一个抽象类,主要用来向 Paging 提供源数据,需要重写 load 方法,在这个方法进行网络请求的处理。需要注意的是 LoadResult.Page 里面的两个参数 prevKey 和 nextKey,这里有个坑。
- prevKey:上一页,设置为空就没有上一页的效果,这需要注意的是,如果是第一页需要返回 null,否则会出现多次请求,我刚开始忽略了,导致首次加载的时候,出现了两次请求。
- nextKey:下一页,设置为空就没有加载更多效果,如果后面没有更多数据设置为空,即滑动到最后不会在加载数据。
load 方法的参数 LoadParams,它是一个密封类,里面有三个内部类 Refresh、Append、Prepend。
类名 作用 Refresh 在初始化刷新的使用 Append 在加载更多的时候使用 Prepend 在当前列表头部添加数据的时候使用
3. 在 Repository 层创建 Pager 和 PagingData
- Pager:是主要的入口页面,在其构造方法中接受 PagingConfig、initialKey、remoteMediator、pagingSourceFactory。
- PagingData:是分页数据的容器,它查询一个 PagingSource 对象并存储结果。
class GitHubRepositoryImpl( |
在 postOfData 方法中构建了一个 Pager, 其构造方法中接受 PagingConfig、initialKey、remoteMediator、pagingSourceFactory,其中 initialKey、remoteMediator 是可选的,pageConfig 和 pagingSourceFactory 必填的。
pagingSourceFactory 是一个 lambda 表达式,在 Kotlin 中可以直接用花括号表示,在花括号内,执行执行网络请求 GitHubItemPagingSource(gitHubApi, 0)
。
最后调用 flow 返回 Flow<PagingData<Value>>
,然后通过 Flow 的 map 方法将数据源 GithubAccountModel 转换成上层用到的 GithubAccount。
关于 flow 在上一篇 Jetpack 成员 Paging3 实践以及源码分析(一) 已经分析过了.
4. 最后一步,接受数据,并绑定 UI
在 ViewModel 接受数据,并传递给 Adapter.
val gitHubLiveData: LiveData<PagingData<GitHubAccount>> = |
LiveData 有三种使用方式,这里演示的是其中一种,其余的在之前的文章
Jetpack 成员 Paging3 实践以及源码分析(一) 已经分析过了。
mMainViewModel.gitHubLiveData.observe(this, Observer { data -> |
到这里请求网络数据并显示的在 UI 上就结束了,最后我们来分析一下 Paging3 内置的错误处理支持,包括刷新和重试等功能。
5. 网络状态异常的处理
Paging3 提供了内置的错误处理支持,包括刷新和重试等功能,说到这里 Google 对于 Paging3 的设计相比于之前的设计真的好,基本上进行网络请求地方用 RecyclerView 去展示数据,都需要用到刷新、重试、错误处理等等功能。
1. 错误处理
Paging3 的组件 PagingDataAdapter,PagingDataAdapter 是一个处理分页数据的可回收视图适配器,PagingDataAdapter 提供了三个方法,如下图所示:
方法名 | 作用 |
---|---|
withLoadStateFooter | 添加列表底部(类似于加载更多) |
withLoadStateHeader | 添加列表的头部 |
withLoadStateHeaderAndFooter | 添加头部和底部 |
Paging3 提供了 LoadStateAdapter 用于实现列表底部和头部样式,只需要继承 LoadStateAdapter 做对应的网络状态处理即可,例如这里实现的 FooterAdapter 加载更多样式。
class FooterAdapter(val adapter: GitHubAdapter) : LoadStateAdapter<NetworkStateItemViewHolder>() { |
在上面分别处理了,正在加载、加载失败并提供重试按钮等等状态。
2. Paging3 同时提供了刷新、重试等等方法,如下图所示:
- refresh:常用用于下拉更新数据。
- retry:常用于底部更多样式,当请求网络失败的时候,显示重试按钮,点击调用 retry。
3. Paging3 还帮我处理了如果出现多次网络请求,只会处理最后一次请求,例如由于网络慢,用户频繁的刷新数据等等
6. 监听网路请求状态
刚才分析过 PagingDataAdapter 是一个处理分页数据的可回收视图适配器,并且还提供了两个监听数据状态的方法。
这两个方法的区别是:
- addDataRefreshListener:当一个新的 PagingData 提交并显示的时候调用。
- addLoadStateListener:这个方法同 addDataRefreshListener 方法,它们之间的区别是 addLoadStateListener 方法返回了一个 CombinedLoadStates 的对象,如上图所示。
CombinedLoadStates 是一个数据类,里面有三个成员变量 refresh、prepend 和 append。
val refresh: LoadState = (mediator ?: source).refresh |
变量 | 作用 |
---|---|
refresh | 在初始化刷新的使用 |
append | 在加载更多的时候使用 |
prepend | 在当前列表头部添加数据的时候使用 |
refresh、prepend 和 append 都是 LoadState 的对象,LoadState 也是一个密封类,每一个 refresh、prepend 和 append 都对应着三种状态。
变量 | 作用 |
---|---|
Error | 表示加载失败 |
Loading | 表示正在加载 |
NotLoading | 表示当前未加载 |
到这里不得不佩服 Google 什么都替我们想好了,这里需要结合自己的项目实际情况,去定制不同的状态处理。
到这里 Paging3 算是完结了,最后贴一下本文案例 Paging3SimpleWithNetWork 已经上传到 GitHub,最后祝大家周末愉快呀。
正在建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,可以前去查看:AndroidX-Jetpack-Practice, 如果这个仓库对你有帮助,请在仓库右上角帮我点个赞,后面我会陆续完成更多 Jetpack 新成员的项目实践
- 本文作者:hi-dhl
- 本文标题:Jetpack 新成员 Paging3 网络实践及原理分析(二)
- 本文链接:https://hi-dhl.com/2020/08/22/jetpack/03-Paging3/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 hi-dhl