为什么使用 Paging 3.0?
如需获取更多内容信息,请查阅 Paging 2.0 到 Paging 3.0 的迁移文档:
置入数据
在您应用的架构方案中,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 需要实现两个抽象方法:
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_INDEXval apiQuery = query + IN_QUALIFIERreturn try {val response = service.searchRepos(apiQuery, position, params.loadSize)val repos = response.itemsval 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)}}
注意,默认情况下,初始加载大小为分页大小的三倍。这样可以保证在列表第一次加载时,即使用户稍作滚动,也能看到足够的数据,从而避免触发太多网络请求。这也是在 PagingSource 实现中计算下一个 Key 时所需要考虑的事情。
getRefreshKey 方法
/* 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 对象
/* Copyright 2020 Google LLC.SPDX-License-Identifier: Apache-2.0 */private const val NETWORK_PAGE_SIZE = 30class 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}}
获取您的数据
后续
推荐阅读