Paging 3.0 简介 | MAD Skills
欢迎阅读 MAD Skills 系列之 Paging 3.0!在本文中,我将介绍 Paging 3.0 并重点说明如何将其集成至您应用的数据层。如果您更喜欢通过视频了解此内容,请在此处查看:
为什么使用 Paging 3.0?
向用户展示一列数据是最常见的 UI 模式之一。当您需要加载大量数据时,可以通过分块异步获取 / 显示数据来提升应用性能。这一模式是如此常见,如果有依赖库可以提供促进实现该模式的抽象,将会为开发者带来巨大的便利。这便是 Paging 3.0 致力解决的用例。作为额外的好处,它还让您的应用可以支持无限的数据集合;而如果您的应用通过网络加载数据,它也为支持本地缓存提供了方便。
如果您正在使用 Paging 2.0,那么 Paging 3.0 也为其前任所包含的功能提供了一系列改进:
- 优先支持 Kotlin 协程和 Flow。
- 支持通过 RxJava Single 或 Guava ListenableFuture 原语进行异步加载。
- 为响应式 UI 设计提供了内建的加载状态和错误信号,包括重试和刷新功能。
- 改进仓库层,包含对于可取消的支持及简化数据源接口。
- 改进表现层、列表分隔符、自定义页面转换以及加载状态头、脚标。
如需获取更多内容信息,请查阅 Paging 2.0 到 Paging 3.0 的迁移文档:
https://developer.android.google.cn/topic/libraries/architecture/paging/v3-migration
置入数据
在您应用的架构方案中,Paging 3.0 最适合作为从数据层获取数据并通过 ViewModel 在 UI 层传输数据来对其进行转换和呈现的一种方式。在 Paging 3.0 中,我们通过名为 PagingSource 的类型访问您的数据层,该类型定义了如何围绕 PagingConfig 所定义的范围获取和刷新数据。
PagingSource 和 Map 类似,都需要定义两个泛型类型: 分页的 Key 的类型和加载的数据的类型。举例来说,从基于 Github API 的页面获取 Repo 项目的 PagingSource 的声明,可以定义为:
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class GithubPagingSource( … ) : PagingSource<Int, Repo>()
△ PagingSource 声明
功能完整的 PagingSource 需要实现两个抽象方法:
- load()
- getRefreshKey()
load 方法
load() 方法正如其名,是由 Paging 库所调用的,用于异步加载要显示的数据的方法。这一方法会在初始加载或者响应用户滑动至边界时调用。load 方法会传入一个 LoadParams 对象,您可以通过它来确定如何触发 load 方法的调用。此对象中包含了有关 load 操作的信息,包括:
- 将要加载的页面的 Key: 如果这是 load 方法第一次被调用 (初始加载),LoadParams.key 将会是 null。在这种情况下,您必须定义初始页面 Key。
- 加载大小: 请求所要加载的项目的数量。
load 方法的返回类型是 LoadResult。它可以是:
- LoadResult.Page: 针对加载成功。
- LoadResult.Error: 针对加载失败。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repo> { val position = params.key ?: GITHUB_STARTING_PAGE_INDEX val apiQuery = query + IN_QUALIFIER return try { val response = service.searchRepos(apiQuery, position, params.loadSize) val repos = response.items val nextKey = if (repos.isEmpty()) { null } else { // 初始加载大小为 3 * NETWORK_PAGE_SIZE // 要保证我们在第二次加载时不会去请求重复的项目。 position + (params.loadSize / NETWORK_PAGE_SIZE) } LoadResult.Page( data = repos, prevKey = if (position == GITHUB_STARTING_PAGE_INDEX) null else position - 1, nextKey = nextKey ) } catch (exception: IOException) { LoadResult.Error(exception) } catch (exception: HttpException) { LoadResult.Error(exception) } }
△ load 方法实现
注意,默认情况下,初始加载大小为分页大小的三倍。这样可以保证在列表第一次加载时,即使用户稍作滚动,也能看到足够的数据,从而避免触发太多网络请求。这也是在 PagingSource 实现中计算下一个 Key 时所需要考虑的事情。
getRefreshKey 方法
刷新 Key 用于 PagingSource.load() 方法后续的刷新调用 (第一次调用是初始加载,使用为 Pager 提供的初始 Key)。每当 Paging 库想要加载新的数据来替代当前列表 (例如,下拉刷新或数据库更新、配置变更、进程终止等情况的发生而导致数据失效) 时,便会发生刷新操作。通常,后续刷新调用会想要重新加载以 PagingState.anchorPosition 为中心的数据,而 PagingState.anchorPosition 则代表了最近所访问的索引位置。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ // 刷新 Key 用于在初始加载的数据失效后下一个 PagingSource 的加载。 override fun getRefreshKey(state: PagingState<Int, Repo>): Int? { // 我们需要获取与最新访问索引最接近页面的前一个 Key(如果上一个 Key 为空,则为下一个 Key) // anchorPosition 即为最近访问的索引 return state.anchorPosition?.let { anchorPosition -> state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) } }
△ getRefreshKey 方法实现
Pager 对象
在定义了 PagingSource 后,我们现在可以创建 Pager 了。Pager 类负责根据 UI 的请求从 PagingSource 中增量拉取数据集合。由于 Pager 需要访问 PagingSource,所以它通常创建在定义 PagingSource 的数据层中。
构造 Pager 所需的另一个类是 PagingConfig,它定义了控制 Pager 获取数据方式的参数。除了必选的 pageSize 参数外,PagingConfig 还暴露了许多可选参数,您可以通过它们微调 Pager 的行为:
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ private const val NETWORK_PAGE_SIZE = 30 class GithubRepository(private val service: GithubService) { fun getSearchResultStream(query: String): Flow<PagingData<Repo>> { Log.d("GithubRepository", "New query: $query") return Pager( config = PagingConfig( pageSize = NETWORK_PAGE_SIZE, enablePlaceholders = false ), pagingSourceFactory = { GithubPagingSource(service, query) } ).flow } }
△ 创建 Pager
上面构造 PagingConfig 的代码中所使用参数的简要说明如下:
- pageSize: 每次要从 PagingSource 加载项目的数量。
- enablePlaceholders: 是否需要 PagingData 为尚未加载的数据返回 null。
通常我们会希望 pageSize 足够的大 (至少足够填充界面的可视区域,但最好是这一数量的 2 到 3 倍),这样 Pager 就不必为了在屏幕上显示足够的内容,而在用户进行滚动操作时一遍又一遍地获取数据了。
获取您的数据
Pager 所产生的类型是 PagingData,该类型提供了进入其背后 PagingSource 的不同窗口。当用户滚动列表时,PagingData 会持续从 PagingSource 中获取数据以提供内容。如果 PagingSource 失效,Pager 会发出一个新的 PagingData 以确保已经分页的项目与 UI 中显示的内容同步。将 PagingData 视为某个时间节点中 PagingSource 的快照可能会对您的理解有所帮助。
由于 PagingSource 是在 PagingSource 失效时发生改变的快照,因此 Paging 库提供了多种以流的形式使用 PagingData 的方式:
- Kotlin Flow 通过 Pager.flow
- LiveData 通过 Pager.liveData
- RxJava Flowable 通过 Pager.flowable
- RxJava Observable 通过 Pager.observable
PagingData 的流可以在展示分页项目到 UI 前通过 ViewModel 进行操作和转换。
后续
按照如上步骤,我们已经将 Paging 3.0 集成到了您应用的数据层中!如何在 UI 中消费 PagingData 以及填充我们的仓库列表,敬请关注我们后续的文章。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
.NET 事件总线 Jaina v1.0.7 发布
Jaina .NET 事件总线,简化项目、类库、线程、服务等之间的通信,代码更少,质量更好。 源码解析 特性 简化组件之间通信 支持事件监视器 支持动作执行器 支持自定义消息存储组件 支持自定义策略执行 支持单消费、多消费消息 支持消息幂等性处理 高内聚,低耦合,使代码更简单 非常快速,每秒可处理30000 +消息 很小,仅10KB 无第三方依赖 可在Windows/Linux/MacOS守护进程部署 支持分布式、集群 高质量代码和良好单元测试 安装 Package Manager Install-Package Jaina .NET CLI dotnet add package Jaina 快速入门 我们在主页上有不少例子,这是让您入门的第一个: 定义事件订阅者ToDoEventSubscriber: // 实现 IEventSubscriber 接口 public class ToDoEventSubscriber : IEventSubscriber { private readonly ILogger<ToDoEventSubscriber> ...
- 下一篇
私域管理平台,LinkWechat V1.5 版本发布
LinkWeChat 基于企业微信开放能力,不仅集成了企微强大的后台管理及基础的客户管理功能,而且提供了多种渠道、多个方式连接微信客户。并通过客情维系、聊天增强等灵活高效的客户运营模块,让客户与企业之间建立强链接,从而进一步通过多元化的营销工具,帮助企业提高客户运营效率,强化营销能力,拓展盈利空间。 此次LinkWechat V1.5版本更新内容如下: 1、系统界面全新设计,更加简洁好用; 2、客户管理再升级,一键查看重复客户; 3、新增客户画像侧边栏,精准运营客户; 3、优化社群运营,新客拉群、老客建群、关键词群、群 SOP 玩转社群营销; 预告:2.0版本预计11月份更新,2.0功能规划
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS8编译安装MySQL8.0.19
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2全家桶,快速入门学习开发网站教程
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2更换Tomcat为Jetty,小型站点的福音