分页库 - 网络 + db 的边界回调,API 获取页面和大小

Posted

技术标签:

【中文标题】分页库 - 网络 + db 的边界回调,API 获取页面和大小【英文标题】:Paging library - Boundary callback for network + db with API taking page and size 【发布时间】:2018-10-31 14:04:55 【问题描述】:

小问题

使用使用页面 + 大小加载新页面的 API 和 BoundaryCallback 类从架构组件处理分页库上的数据库 + 网络的正确方法是什么?

研究与解释

目前,用于架构组件的分页库中的类BoundaryCallback 接收列表中元素的实例作为参数,而没有该元素所在位置的实际上下文。它发生在onItemAtFrontLoadedonItemAtEndLoaded

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

由于 Api 需要提供页码和页面大小,我看不到仅通过接收onItemAtFrontLoaded 和 @ 中提供的列表中的一个元素来将其发送到 Api 的方法987654330@。检查this link 中的谷歌示例,他们使用最后一个元素的名称来获取下一个元素,但这不适合页面+大小的 Api。

他们还有另一个示例,仅使用 PagedKeyedDatasource 的网络,但没有示例或线索说明如何将其与数据库和 BoundaryCallback 混合。

编辑: 到目前为止,我发现的唯一解决方案是将最后加载的页面存储在共享首选项中,但这听起来像是一个肮脏的把戏。

参考 https://github.com/googlesamples/android-architecture-components/issues/252#issuecomment-392119468 获取官方意见。

【问题讨论】:

真正的 问题是,如果您在服务器端有新元素,则每个项目所在的页面都会发生变化。您能否将基于 pageIndex 的 API 长期可靠地缓存到 DB 中? 如果元素按时间排序(最近的最后一页),或者像我的情况一样,生成的提要每天保持一次不变,则可能。有多个应用程序基于大小 + 页码的页面索引是有意义的,我已经看到许多 API 实现了这种模式 没有,只有官方支持评论里的那个 这取决于 API 响应是否提供元数据,例如 pagepages。当它使用键来获取下一页时,这似乎针对 noSQL 进行了优化 - 而对于 SQL,这不一定有意义......并且当遵循来自 GitHub cmets 的逻辑时;如果密钥同时被删除怎么办? 从现在开始你找到解决办法了吗? 【参考方案1】:

documentation 在这个问题上是这样说的:

如果您没有使用 item-keyed network API,您可能正在使用 页面键控或页面索引。如果是这种情况,分页库 不知道 BoundaryCallback 中使用的页面键或索引, 所以你需要自己跟踪它。您可以通过以下两种方式之一执行此操作:

本地存储页面键

如果您想完美地恢复您的查询, 即使应用程序被杀死并恢复,您也可以将密钥存储在磁盘上。 请注意,使用位置/页面索引网络 API,有一个简单的 通过使用 listSize 作为下一次加载的输入(或 listSize / NETWORK_PAGE_SIZE,用于页面索引)。当前列表 size 虽然没有传递给 BoundaryCallback。这是因为 PagedList 不一定知道本地的项目数 贮存。占位符可能被禁用,或者数据源可能不计算在内 项目总数。

相反,对于这些位置情况,您可以在数据库中查询 项目的数量,并将其传递给网络。

内存中的页面键

查询下一页通常没有意义 如果您获取的最后一页已加载数小时,则来自网络或 几天前。如果您将密钥保留在内存中,则可以随时刷新 您从网络源开始寻呼。将下一个键存储在内存中, 在您的 BoundaryCallback 内。当你创建一个新的 BoundaryCallback 创建 PagedList 的新 LiveData/Observable 时,刷新数据。 例如,在 Paging Codelab 中,GitHub 网络页面索引为 存储在内存中。

以及指向示例 Codelab 的链接:https://codelabs.developers.google.com/codelabs/android-paging/index.html#8

【讨论】:

【参考方案2】:

我有一个类似的 API(pageNum + 大小),我的 data classpageNumpageSize 中有 2 个额外字段,默认分别为 1PAGE_SIZE

如果您使用的是Network+DB 那么你将拥有onZeroItemsLoadedonItemAtEndLoaded, 在onZeroItemsLoaded 中按原样发送pageNumpageSize,在onItemAtEndLoaded 中将pageSize 增加1 然后发送。

假设您有一个方法fetchData(pageNum, pageSize),当您收到结果时,只需在此页面的每个项目中相应地更新pageNumpageSize

【讨论】:

当你杀死你的应用程序然后打开它并且应用程序从数据库加载数据时会发生什么? pageNum 将无效... 我有 pageNum 作为表中的一个字段,所以当你杀死应用程序并重新打开时,如果 Db 为空 onZeroItem 将被调用,使用 pageNum=1,pageSize=PAGE_SIZE,如果 onItemAtEndLoaded 是调用(您还可以在此方法中将最后一项作为参数获取),使用 DB 中最后一项的 (pageNum+1)。 感谢您的澄清,起初我不明白。在我的示例中,这意味着迭代从一个 api 响应页面返回的 20 个项目,并将 pageNum 设置为每个项目,然后再插入数据库。这似乎不是一个很好的解决方案。我搞乱了使用observeForever 监听具有页码的单独实体。 link 不知道这是不是好方法... 我认为在异步执行时迭代超过 20 个项目并不昂贵(以及您的 API 调用可能也是异步的)。但最终,这取决于你。您可以使用更多方法,例如自动增加表中的 pageNum 字段,然后计算 pageNum,只需使用 PAGE_SIZE 取其 MOD,然后加 1(可能需要在这里和那里调整)。编辑:刚刚看到你也在开发一个电影应用程序,你可以用我上面讨论的相同方法检查我的github.com/Kashish-Sharma/TheMovieDBApp 很好,当我遇到困难时,请保留它以供参考。谢谢。【参考方案3】:

我实现了这个:

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;

【讨论】:

【参考方案4】:

假设您总是从服务器获取每页的N=10 项,然后将其存储在数据库中。您可以使用 sql 查询 SELECT COUNT(*) FROM tbl 获取 db 中的项目数并将其存储在变量 count 中。 现在要获取接下来应该请求的页码,请使用:

val nextPage: Int = (count / N) + 1

【讨论】:

以上是关于分页库 - 网络 + db 的边界回调,API 获取页面和大小的主要内容,如果未能解决你的问题,请参考以下文章

在分页库 3 中恢复滚动位置

分页库仅在房间数据库中存储第一次获取的值

分页库过滤器/搜索

如何使用 codeigniter 分页库回显页码?

使用分页库显示加载指示器

使用 Google 的分页库更新 PagedList 中的单个项目