Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to observe a liveData only for the new update after the subscription to the liveData

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()
        }
    }
}
like image 932
lannyf Avatar asked May 22 '18 19:05

lannyf


People also ask

How do I update my LiveData list?

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.

In which method should you observe 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?

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.

Why LiveData Observer is being triggered twice?

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.


1 Answers

You can use SingleLiveEvent

SingleLiveEvent

like image 51
katharmoi Avatar answered Nov 15 '22 01:11

katharmoi