Mathbl
Mathbl

Reputation: 5057

Android MVVM - How to make LiveData emits the data it has (forcing to trigger the observer)

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?

Upvotes: 18

Views: 8611

Answers (3)

Gk Mohammad Emon
Gk Mohammad Emon

Reputation: 6938

I do force refresh by this approach -

if (mainViewModel.mutable != null && mainViewModel.mutable.getValue()) {
    mainViewModel.mutable.setValue(mainViewModel.mutable.getValue());
}

Upvotes: 1

Prasanna Narshim
Prasanna Narshim

Reputation: 798

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!!

Upvotes: 30

Hong Duan
Hong Duan

Reputation: 4294

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
}

Upvotes: 5

Related Questions