exposes an observable query which is re-queried when a write „invalidates” the table, then we don’t need to manage „data loading callbacks” - threading is simplified: data loading is automatic and occurs on a background thread (initial evaluation + triggered by future writes)
result as LiveData (which retains previous value and can be observed) and updates it • ViewModel caches the LiveData across configuration changes • LiveData pauses operations after onStop until onStart via Lifecycle events, and automatically unsubscribes on onDestroy, emits new values
* FROM ${Cat.TABLE_NAME} ORDER BY ${Cat.COLUMN_RANK}") fun listenForCats(): LiveData<List<Cat>> } • What if I have MANY (10000+) cats? • What if I update it often? • How many items should I read to memory? Should I read all items when a write happens?
result is a lazy-loaded „cursor” • Cursors are invalidated and re-calculated on writes, making „listening for changes” possible • Everything is a proxy, no data is kept in memory – everything is loaded from DB
data set must still evaluate the entirety of the cursor (in SQLite’s case, only a window is filled up, but subsequent loads would happen on UI thread) – Query result is evaluated on background thread, but in Realm’s case, the accessors are on main thread, and it is not a completely free operation
pages to memory that we actually need – reading the pages (and elements) should occur on background thread • Paging Library gives us: – PagedList: exposes data – DataSource: fills paged list – DataSource.Factory: creates a data source – PagedListAdapter: consumes paged list – LivePagedListBuilder: creates new datasource after invalidation
[n, n+1, ...] • ItemKeyed: item in page at index[n-1] has a value that allows us to load the page that contains [n, n+1, ...] • PageKeyed: page contains a „cursor” value that allows us to request „the next page”
invalidated • With a DataSource.Factory, the DataSource can be re-created when it is invalidated • (Invalidation happens when a write has changed the data set in such a way that a re- evaluation is required)
This is where we can start network requests to fetch the next batch of cats when we’ve reached the end of what’s stored in the database • The callback can be called multiple times, so we should ensure that we don’t execute the same network request multiple times • We can set this on the LivePagedListBuilder
and a LoadCallback (or LoadRangeCallback for positional) • We can extend PositionalDataSource, ItemKeyedDataSource or PageKeyedDataSource, and implement the right methods
void onResult( @NonNull List<Value> data, int position, int totalCount); – Load callback: public abstract void onResult( @NonNull List<Value> data); – Also: it also has Key getKey(T item) that lets us obtain the item key as a load parameter
source via a DataSource.Factory that is wrapped in a LivePagedListBuilder, then we can invalidate the data source, and re-retrieve the initial page class CatDataSourceFactory( private val catApi: CatApi ) : DataSource.Factory<String, Cat>() { override fun create(): DataSource<String, Cat> = CatDataSource(catApi) }
loading indicator while the datasource is retrieving data (especially if downloading from network) • Solution: – we need to expose the „latest loading status” of „the latest data source” that was created by the factory – The data source can expose status via LiveData – The factory can expose data source via LiveData
latest data source, it is possible to expose a callback that fetches the next page Retry: dataSourceFactory.latestLiveData.value? .retry() Refresh: dataSourceFactory.latestLiveData.value? .refresh() // invalidate
expose “current error value” as a LiveData from the DataSource • Transformations.switchMap() from the DataSource within the DataSource.Factory’s LiveData, again
provides DiffUtil.ItemCallback • ViewModel holds DataSource.Factory, builds LiveData<PagedList<T>> using LivePagedListProvider (with provided PagedList.BoundaryCallback for fetching new data), exposes DataSource’s LiveDatas via switchMap() • Fragment observes LiveData exposed from ViewModel, feeds PagedList to PagedListAdapter