Библиотека пейджинга - Граничный обратный вызов для сети + db с API, использующим страницу и размер

Короткий вопрос:

Каков правильный способ обработки базы данных + сети в библиотеке Paging из компонентов архитектуры, используя API, который использует размер страницы + для загрузки новой страницы и класса BoundaryCallback?

Исследования и объяснения

В настоящее время класс BoundaryCallback используемый в библиотеке подкачки для компонентов архитектуры, получает в качестве параметра экземпляр элемента в списке без фактического контекста того, где находится этот элемент. Это происходит в onItemAtFrontLoaded и onItemAtEndLoaded.

Мой Api должен получать страницу и размер страницы для загрузки следующего фрагмента данных. Граничный обратный вызов, добавленный как часть построителя списков выведенных страниц, должен сообщать вам, когда загружать следующую страницу данных на основе расстояния предварительной выборки и размера страницы.

Поскольку Api нуждается в номере страницы и размере страницы для предоставления, я не вижу способа отправить это Api, просто получив один из элементов из списка, предложенный в onItemAtFrontLoaded и onItemAtEndLoaded. Проверяя примеры Google в этой ссылке, они используют имя последнего элемента для получения следующего, но это не соответствует Api с размером страницы +.

У них также есть другой пример с только сетью, которая использует PagedKeyedDatasource, но нет образца или подсказки о том, как смешивать это с базой данных и BoundaryCallback.

Изменить: только решения, которые я нашел до сих пор, - это сохранить последнюю загруженную страницу в общих настройках, но это звучит как грязный трюк.

Обратитесь к https://github.com/googlesamples/android-architecture-components/issues/252#issuecomment-392119468 за официальный ввод.

Ответ 1

Я реализую это:

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;
}

Ответ 2

Допустим, вы всегда выбираете N=10 элементов на страницу с сервера, а затем сохраняете их в дБ. Вы можете получить количество элементов в db, используя SQL-запрос SELECT COUNT(*) FROM tbl и сохранить его в переменной count. Теперь, чтобы получить номер страницы, которая должна быть запрошена далее, используйте:

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

Ответ 3

В документации есть что сказать по этому вопросу:

Если вы не используете сетевой API с ключом элемента, возможно, вы используете страницу с ключом или индексированную страницу. В этом случае библиотека подкачки не знает о ключе страницы или индексе, используемом в BoundaryCallback, поэтому вам нужно отследить его самостоятельно. Вы можете сделать это одним из двух способов:

Ключ страницы локального хранилища

Если вы хотите полностью возобновить запрос, даже если приложение было убито и возобновлено, вы можете сохранить ключ на диске. Обратите внимание, что с помощью сетевого API позиционного индекса/индекса страницы существует простой способ сделать это, используя listSize в качестве входных данных для следующей загрузки (или listSize/NETWORK_PAGE_SIZE для индексации страницы). Текущий размер списка не передается в BoundaryCallback. Это связано с тем, что PagedList не обязательно знает количество элементов в локальном хранилище. Заполнители могут быть отключены или DataSource может не учитывать общее количество элементов.

Вместо этого для этих позиционных случаев вы можете запросить в базе данных количество элементов и передать их в сеть.

Ключ страницы в памяти

Часто нет смысла запрашивать следующую страницу из сети, если последняя загруженная страница была загружена много часов или дней назад. Если вы храните ключ в памяти, вы можете обновлять его каждый раз, когда начинаете пейджинг из сетевого источника. Сохраните следующий ключ в памяти, внутри вашего BoundaryCallback. При создании нового BoundaryCallback при создании нового LiveData/Observable из PagedList обновите данные. Например, в Paging Codelab индекс сетевой страницы GitHub хранится в памяти.

И ссылки на пример Codelab: https://codelabs.developers.google.com/codelabs/android-paging/index.html#8

Ответ 4

Главное - сделать базу данных источником истины для данных, отображаемых пользовательским интерфейсом, и она поддерживается сетевыми запросами. BoundaryCallback имеет два метода: onZeroItemsLoaded(), который вызывается, когда в нашей базе данных есть нулевой элемент, и onItemAtEndLoaded(), который вызывается, когда мы достигаем последнего элемента в базе данных. в обоих случаях мы вызываем вспомогательный метод, который делает запрос сервера и сохраняет ответ в базе данных. после изменения содержимого базы данных LiveData выдает PagedList с полным результатом запроса, и вы можете настроить pagedList так, как вы хотите, создав его с помощью LivePagedListBuilder

проверьте этот обновленный codelab, поскольку он отвечает на все ваши вопросы