分页库 - 使用API获取页面和大小的网络+数据库边界回调

72

简短问题:

如何正确处理使用页码和大小加载新页面的API,以及使用BoundaryCallback类在Architecture组件的Paging库中处理数据库+网络?

研究和解释

目前,在用于Architecture组件的Paging库中使用的BoundaryCallback类,接收的参数是列表中元素的实例,而不是该元素所在位置的实际上下文。这发生在onItemAtFrontLoadedonItemAtEndLoaded

我的API应该接收页面和页面大小来加载下一个数据块。作为分页列表构建器的一部分添加的边界回调应该基于预取距离和页面大小告诉您何时加载下一页数据。

由于API需要提供页面编号和页面大小,因此我看不到通过仅从onItemAtFrontLoadedonItemAtEndLoaded中提供的列表中的一个元素发送它的方法。检查此链接中的Google示例,他们使用最后一个元素的名称来获取下一个元素,但这并不适用于具有页面+大小的API。

他们还有另一个只使用PagedKeyedDatasource的网络示例,但是没有关于如何将其与数据库和BoundaryCallback混合的示例或线索。 编辑: 到目前为止,我找到的唯一解决方案是在共享首选项中存储最后加载的页面,但这听起来像是个小技巧。
请参考官方意见https://github.com/googlesamples/android-architecture-components/issues/252#issuecomment-392119468

真正的问题是,如果服务器端有新元素,每个项目所在的页面将会发生变化。您能否可靠地将基于“pageIndex”的API缓存到数据库中长期保存? - EpicPandaForce
如果元素按时间排序(最近的在最后一页),或者像我的情况一样,生成的源每天保持不变,则这是可能的。有多种应用程序可以基于大小+页码的页面索引,我已经看到许多API实现了这种模式。 - droidpl
不,只有官方支持评论中的那一个。 - droidpl
如果API响应提供了类似于“page”和“pages”的元数据,则情况取决于它。当它使用键来获取下一页时,这似乎是针对noSQL进行优化的,而对于SQL来说,这不一定有意义...根据GitHub评论的逻辑,如果该键已被删除,那怎么办? - Martin Zeitler
你现在找到任何解决方案了吗? - Kapta
4个回答

1

文档对此问题有如下说明:

如果您没有使用基于项目键的网络API,那么您可能正在使用基于页面键或页面索引。如果是这种情况,分页库不知道在BoundaryCallback中使用的页面键或索引,因此您需要自己跟踪它。有两种方法可以做到这一点:
本地存储页面键
如果您想完美地恢复查询,即使应用程序被杀死并恢复,您也可以将键存储在磁盘上。请注意,对于位置/页面索引网络API,有一种简单的方法可以通过使用listSize作为下一个加载的输入来实现此目的(或者对于页面索引,listSize / NETWORK_PAGE_SIZE)。当前列表大小没有传递给BoundaryCallback。这是因为PagedList不一定知道本地存储中的项目数量。占位符可能已禁用,或者DataSource可能不计算总项目数。对于这些位置情况,您可以查询数据库以获取项目数,并将其传递给网络。
内存中的页面键
通常,如果您从网络源获取的最后一页是在许多小时或几天之前加载的,则从网络查询下一页并没有意义。如果将键保存在内存中,则可以在任何时候刷新,从网络源开始分页。将下一个键存储在内存中,在创建新的LiveData/Observable of PagedList时创建新的BoundaryCallback时,刷新数据。例如,在Paging Codelab中,GitHub网络页面索引存储在内存中。

以下是一个示例Codelab的链接:https://codelabs.developers.google.com/codelabs/android-paging/index.html#8


1
我有一个类似的 API(pageNum + size),在我的数据类中有两个额外的字段 pageNumpageSize,默认值分别为 1PAGE_SIZE。如果您使用的是 Network+DB,则会有 onZeroItemsLoadedonItemAtEndLoaded,在 onZeroItemsLoaded 中发送 pageNumpageSize,在 onItemAtEndLoaded 中将 pageSize 增加 1 然后发送。假设您有一个方法 fetchData(pageNum, pageSize),当您收到结果时,只需在此页面的每个项目中相应地更新 pageNumpageSize

当你关闭应用程序并稍后重新打开它时,应用程序从数据库加载数据会发生什么?pageNum将无效... - Nikola Srdoč
1
我在表中有一个名为pageNum的字段,因此当您关闭应用程序并重新打开时,如果Db为空,将调用onZeroItem方法,使用pageNum = 1、pageSize = PAGE_SIZE,如果调用onItemAtEndLoaded方法(在该方法中,您还会获得最后一项作为参数),请使用从DB中最后一项获取的(pageNum + 1)。 - Kashish Sharma
感谢您的澄清,一开始我并没有理解。在我的例子中,这意味着要遍历一个API响应页面返回的20个项目,并在插入到数据库之前为每个项目设置pageNum。这似乎不是一个很好的解决方案。我正在尝试使用observeForever监听具有页码的单独实体。链接不知道这是否是一个好方法... - Nikola Srdoč
我认为异步迭代20个项目并不昂贵(伴随着你的 API 调用,这也可能是异步的)。但最终,这取决于你。 你可以使用更多的方法,比如在表中自动递增 pageNum 字段,然后计算 pageNum,只需将其与 PAGE_SIZE 取模,然后加 1(可能需要在这里和那里进行调整)。 编辑:刚刚看到你也正在开发一款电影应用程序,你可以检查一下我的应用程序,采用了我上面讨论的相同方法 https://github.com/Kashish-Sharma/TheMovieDBApp - Kashish Sharma
好的,如果我遇到问题,我会把它作为参考。谢谢。 - Nikola Srdoč

0
假设您总是从服务器获取每页N=10个项目,然后将其存储在数据库中。您可以使用SQL查询SELECT COUNT(*) FROM tbl获取数据库中的项目数量,并将其存储在变量count中。 现在要获取下一个应请求的页面编号,请使用:
val nextPage: Int = (count / N) + 1

0
我实现了这个:
PagedList.BoundaryCallback<Produto> boundaryCallbackNovidades = new PagedList.BoundaryCallback<Produto>(){
    int proxPagina;
    boolean jaAtualizouInicio=false;

    public void onZeroItemsLoaded() {
        requestProdutos(
            webService.pesquisarNovidadesDepoisDe(LocalDateTime.now().format(Util.formatterDataTime), 0, 20));
    }

    public void onItemAtFrontLoaded(@NonNull Produto itemAtFront) {
        if(!jaAtualizouInicio)
            requestProdutos(
                webService.pesquisarNovidadesMaisRecentesQue(itemAtFront.data.format(Util.formatterDataTime)));
        jaAtualizouInicio=true;
    }

    public void onItemAtEndLoaded(@NonNull Produto itemAtEnd) {
        requestProdutos(
            webService.pesquisarNovidadesDepoisDe(LocalDateTime.now().format(Util.formatterDataTime), proxPagina++, 20));
    }
};


public LiveData<PagedList<Produto>> getNovidades(){
    if(novidades==null){
        novidades = new LivePagedListBuilder<>(produtoDao.produtosNovidades(),
                10)
                .setBoundaryCallback(boundaryCallbackNovidades)
                .build();
    }
    return novidades;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接