Is it a good practice to observeForever in Repository class? db+network paged list
A

1

4

Im building an app following architecture guidelines.Implemented room db caching + network.Need to get latest page number from separate entity.

My model:

@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 class contains data which i display in my paged list which observes changes from db.

Using PagedList.BoundaryCallback i need to fetch new data from network and insert it into db. But i need to get page number somehow. My dao:

@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();

I was thinking to observe Top_Rated_Movies_Page from db in my repository class with observeForever() to get that page number. Is that the best way to approach this?

Afterimage answered 11/6, 2020 at 9:26 Comment(1)
Theoretically whenever you need to use observeForever and not have a LifecycleOwner, you're supposed to use Transformations.switchMap instead.Cliffhanger
P
2

Since the only time you'll read the next page key or update the backing DB is through BoundaryCallback, you can just read / write your next page key directly.

So in your onItemAtEndLoad() implementation you want something like:

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

Your DAO:

@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);

And don't forget to clear out both the items and remoteKey whenever you expect to refresh the data!

In the case where you want to keep track of different keys for query, you can simply add that column to your RemoteKey entity.

FYI: Paging2 has been superseded by Paging3 (though just launched alpha01), here is the similar Paging3 sample which solves exactly your use-case: https://github.com/android/architecture-components-samples/blob/master/PagingWithNetworkSample/app/src/main/java/com/android/example/paging/pagingwithnetwork/reddit/repository/inDb/PageKeyedRemoteMediator.kt

Papillose answered 12/6, 2020 at 20:32 Comment(2)
when db.runInTransaction gets called to get nextMoviePage by passing new Callable i got "Cannot access database on the main thread error".How to make nextMoviePage available immediately after onItemAtEndLoaded gets called?Susceptive
There's no way to force room to run on the main thread as it would lock up the UI, but you don't need to for ordering guarantees in this case.Papillose

© 2022 - 2024 — McMap. All rights reserved.