I have this ViewModel
that gets a list from the network and I populate a RecyclerView
with the data (MyAvailabilityRepository
returns a MutableLiveData
, that's why i'm using Transformations.switchMap
):
class MyAvailabilityViewModel : ViewModel() {
private val getListsParams = MutableLiveData<String>()
private val getListsObservable = Transformations.switchMap(getListsParams) {
organizationId -> MyAvailabilityRepository.getSectionedLists(organizationId)
}
fun getListsObservable() : LiveData<Resource<MutableList<SectionedAvailabilityList>>> {
return getListsObservable
}
fun fetchLists(organizationId: String, forceRefresh: Boolean = false) {
if (getListsParams.value == null || forceRefresh) {
getListsParams.value = organizationId
}
}
}
Fragment's onActivityCreated
:
override fun onActivityCreated(savedInstanceState: Bundle?) {
...
viewModel.getListsObservable().observe(this, Observer { // populate RecyclerView })
viewModel.fetchLists(organizationId)
}
Since getListParams.value
is null the first time, it will set getListsParams.value = organizationId
and trigger the switchMap
and call the repository to get the list from the network.
When I want to force a refresh (by pull-to-refresh) and call the network again, I can use forceRefresh = true
:
override fun onRefresh() {
viewModel.fetchLists(organizationId, forceRefresh = true)
}
It will set the value of organizationId
and trigger the Transformations that will then call network.
But, I have a scenario where I clear the data from my RecyclerView's adapter. If after that, the user click a button, I would like to trigger the observer again so that I re-populate the adapter with the data that the getListsObservable has already fetched. I don't want to call forceRefresh on this one cause i'm sure I already have the data and I would just like to trigger the observer again so that my UI is updated with the existing data. Since getListParams.value
is not null at that point, then nothing happens when I call fetchLists(organizationId)
later on.
Any idea of how I could achieve that with my current setup?
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.
ViewModel allows the app's data to survive configuration changes. In this codelab, you'll learn how to integrate LiveData with the data in the ViewModel . The LiveData class is also part of the Android Architecture Components and is a data holder class that can be observed.
Attach the Observer object to the LiveData object using the observe() method. The observe() method takes a LifecycleOwner object. This subscribes the Observer object to the LiveData object so that it is notified of changes. You usually attach the Observer object in a UI controller, such as an activity or fragment.
You should manually call removeObserver(Observer) to stop observing this LiveData. While LiveData has one of such observers, it will be considered as active. If the observer was already added with an owner to this LiveData, LiveData throws an IllegalArgumentException .
Yes, the answer given by Hong Duan is perfect. I am just going to extend that answer here,
The better way of doing is having an extension function.
My extension function looks like this,
fun <T> MutableLiveData<T>.forceRefresh() {
this.value = this.value
}
Caller function looks like this,
mutableLiveDataObject.forceRefresh()
Happy Coding!!
Try removeObservers()
and observe()
again:
viewModel.getListsObservable().removeObservers(this)
viewModel.getListsObservable().observe(this, Observer { // populate RecyclerView })
because:
If LiveData already has data set, it will be delivered to the observer.
See the docs.
Or maybe you can change the getListsObservable()
to return a MutableLiveData
, then call setValue
manually:
fun loadCurrentData() {
getListsObservable.value = getListsObservable.value
}
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