using PagedList, and here it does not have database back, but a data list (call it CachedDataList
) in the memory which could be filled in by fetchMore()
function.
Having the PositionalDataSource
, DataSource.Factory
and PagedList.BoundaryCallback
, it works but one issue here.
The flow is the PositionalDataSource's loadInitial()
will be called at beginning to start to load data from the CachedDataList
, and call loadRange()
after that to continue loading data from the CachedDataList
by page size.
When all data from the CachedDataList
are paged off, the BoundaryCallback::onItemAtEndLoaded()
will be called (if there is no backing data at beginning then the BoundaryCallback::onZeroItemsLoaded()
is called),
In there it will start to asking fetchMore to append more data into the CachedDataList
, and when the new data is appended to it then call the DataSource's invalidate()
to restart the new PagedList and DataSource pair, and starting from PositionalDataSource's loadInitial()
again.
It is done by
observableCachedData.observe(owner, refreshHandler!!)
//??? TODO: how to only listen to newly posted data after
//starting the observe?
//DOC: 'If LiveData already has data set, it will be delivered to the observer.'
// fetchMore
val didFetch = dataRequester.fetchMore() //asyc call
here, it observes the observableCachedData's change, and if there is change then the onChanged()
of the
class RefreshHandler(val observableCachedData: MutableLiveData<List<Data>>) : Observer<List<Data>> {
override fun onChanged(datalist: List<Data>?)
will be called, and in which to call the DataSource's invalidate()
but the subscription of observableCachedData.observe()
causes the refreshHandler
called immediately (it's by design as stated in the DOC), this behavior is not desired here since we want the handler is called when the new data is append to the CachedDataList
.
i.e. the CachedDataList
had 30 data, when do fetchMore() there will be another 30 data appended to it, become 60. But this onChange()
is called with data still at 30 (the append has not been coming yet).
Is there a way to subscribe to a live data but only get notified for update that happened after it is subscribed to it?
class DataBoundaryCallback(
private val owner: LifecycleOwner,
private val dataRequester: FetchMoreRequester,
private val dataSourceFactory: DataSourceFactory?
) : PagedList.BoundaryCallback<IData>() {
private var hasRequestInProgress = false
override fun onZeroItemsLoaded() {
requestAndSaveData()
}
override fun onItemAtEndLoaded(itemAtEnd: IData) {
requestAndSaveData()
}
private fun requestAndSaveData() {
if (hasRequestInProgress) return
hasRequestInProgress = true
// ask dataRequester to fetchMore
// setup observer
val cachedDataList = dataRequester.getCachedLiveData()
val observableCachedData = cachedDataList.getLiveData()
refreshHandler = RefreshHandler(observableCachedData)
observableCachedData.observe(owner, refreshHandler!!) //??? TODO: how to only listen to newly posted data after starting the observe? DOC: 'If LiveData already has data set, it will be delivered to the observer.'
// fetchMore
val didFetch = dataRequester.fetchMore()
if (!didFetch) { //not stated fetch
hasRequestInProgress = false
observableCachedData.removeObserver(refreshHandler!!)
}
}
var refreshHandler : RefreshHandler? = null
inner class RefreshHandler(val observableCachedData: MutableLiveData<List<Data>>) : Observer<List<Data>> {
override fun onChanged(datalist: List<Data>?) {
observableCachedData.removeObserver(refreshHandler!!)
val dataSource = dataSourceFactory.getPositionalDataSource()
// to start a new PagedList and DataSource pair flow
// and trigger the DataSource's loadInitial()
dataSource.invalidate()
}
}
}
Update LiveData objectsLiveData has no publicly available methods to update the stored data. The MutableLiveData class exposes the setValue(T) and postValue(T) methods publicly and you must use these if you need to edit the value stored in a LiveData object.
Google introduced Android architecture components which are basically a collection of libraries that facilitate robust design, testable, and maintainable apps. It includes convenient and less error-prone handling of LifeCycle and prevents memory leaks.
How do you encapsulate the LiveData stored in a ViewModel so that external objects can read data without being able to update it? Inside the ViewModel, change the data type of the property to be LiveData and make it private. Use a backing property to expose a read-only property of type MutableLiveData.
The first time it is called upon startup. The second time it is called as soon as Room has loaded the data. Hence, upon the first call the LiveData object is still empty. It is designed this way for good reasons.
You can use SingleLiveEvent
SingleLiveEvent
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With