Является ли хорошей практикой наблюдать Forever в классе Repository? db+сетевой постраничный список

Я создаю приложение в соответствии с рекомендациями по архитектуре. Реализовано кэширование БД комнаты + сеть. Необходимо получить последний номер страницы из отдельного объекта.

Моя модель:

@Entity(tableName = "top_rated_movie_page")
public class Top_Rated_Movies_Page {

@PrimaryKey(autoGenerate = true)
private int db_id;
private Integer page;
private Integer total_results;
private Integer total_pages;
@Ignore
private List<Result> results;
...

Result содержит данные, которые я отображаю в своем постраничном списке, который наблюдает за изменениями из db.

Используя PagedList.BoundaryCallback, мне нужно получить новые данные из сети и вставить их в БД. Но мне нужно как-то получить номер страницы. Мой дао:

@Insert
void insertAll(Top_Rated_Movies_Page page,List<Result> top_rated_results);

@Query("SELECT * FROM Result")
DataSource.Factory<Integer, Result> getAllResults();

@Query("SELECT * FROM top_rated_movie_page WHERE page= (SELECT MAX(page) FROM top_rated_movie_page)")
LiveData<Top_Rated_Movies_Page> getMoviePage();

Я думал наблюдать Top_Rated_Movies_Page из db в моем классе репозитория с observeForever(), чтобы получить этот номер страницы. Это лучший способ приблизиться к этому?


person Nikola Srdoč    schedule 11.06.2020    source источник
comment
Теоретически всякий раз, когда вам нужно использовать observeForever и не иметь LifecycleOwner, вместо этого вы должны использовать Transformations.switchMap.   -  person EpicPandaForce    schedule 11.06.2020


Ответы (1)


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

Итак, в вашей реализации onItemAtEndLoad() вы хотите что-то вроде:

String nextMoviePage = db.runInTransaction(() -> {
    movieDao.nextRemoteKey().key;
});

// Make sure not to run on main thread
MovieNetworkResponse response = networkApi.fetchNextMoviePage(remoteKey);

db.runInTransaction(() -> {
    movieDao.clearAll(); // Remove previous key
    movieDao.insertKey(RemoteKey(response.nextPageKey)); // Insert new key
    movieDao.insertAll(response.movies); // Update DataSource + invalidate()
});

Ваш ДАО:

@Insert
void insertAll(List<Result> top_rated_results);

@Query("SELECT * FROM Result")
DataSource.Factory<Integer, Result> getAllResults();

@Query("SELECT * FROM result_key LIMIT 1")
String nextRemoteKey();

@Insert
void insertKey(RemoteKey remoteKey);

И не забывайте очищать как элементы, так и remoteKey всякий раз, когда вы ожидаете обновления данных!

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

К вашему сведению: Paging2 был заменен Paging3 (хотя только что запущенный alpha01), вот аналогичный образец Paging3, который решает именно ваш вариант использования: https://github.com/android/architecture-components-samples/blob/master/PagingWithNetworkSample/app/src/main/java/com/android/example/paging/pagingwithnetwork/reddit/repository/inDb/PageKeyedRemoteMediator.kt

person dlam    schedule 12.06.2020
comment
когда db.runInTransaction вызывается для получения nextMoviePage путем передачи new Callable, я получаю сообщение Не могу получить доступ к базе данных в ошибке основного потока. Как сделать nextMoviePage доступным сразу после вызова onItemAtEndLoaded? - person Nikola Srdoč; 13.06.2020
comment
Невозможно заставить пространство работать в основном потоке, так как это заблокирует пользовательский интерфейс, но в этом случае вам не нужно заказывать гарантии. - person dlam; 13.06.2020