86304

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

Question:

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?

Answer1:

Try removeObservers() and observe() again:

viewModel.getListsObservable().removeObservers(this) viewModel.getListsObservable().observe(this, Observer { // populate RecyclerView })

because:

<blockquote>

If LiveData already has data set, it will be delivered to the observer.

</blockquote>

See the <a href="https://developer.android.com/reference/android/arch/lifecycle/LiveData#observe(android.arch.lifecycle.LifecycleOwner,%20android.arch.lifecycle.Observer%3CT%3E)" rel="nofollow">docs</a>.

Or maybe you can change the getListsObservable() to return a MutableLiveData, then call setValue manually:

fun loadCurrentData() { getListsObservable.value = getListsObservable.value }

Answer2:

Yes, the answer given by Hong Duan is perfect. I am just going to extend that answer here,

The better of way 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!!

Recommend

  • Turning a mobile phone into a beacon
  • Kafka - Retention period Parameter
  • What's the role of EX stage for branching in Pipelined MIPS w Forwarding?
  • CSS Flex - I have a list of items can need to make them horizontally scroll with overflow hidden
  • Can not open created raster in R
  • Files:insert - Google Drive SDK - Python Example - What is Drive API service instance?
  • iPad launch image problem
  • Create ListView ScrollBar Appeared Event
  • Use sharepoint cross-domain javascript library to consume REST API from localhost
  • python: unpacking a string to a list
  • FB wall posting OAuthException
  • @org.omnifaces.cdi.ViewScoped invokes @PostConstruct on unload of an already destroyed view
  • Python3 Windows multiprocessing passing socket to process
  • how can i have scrollbar when position is negative?
  • unable to set Row.Readonly=false in Datagridview in winforms
  • How to get service executable file path
  • Filter a directory for .csv files using c# lambda
  • AngularJS Radio group not setting $dirty on field
  • R to BigQuery Data Upload Error
  • Comparing a large set of images by content
  • Google Geocoding API limit exceeded on cell network, but not on wifi
  • How to adapt DirectX-style world/view/projection matrices to OpenGL?
  • Insert statement not working using execute(array()) of PDO Extension
  • Android NDK refer to external libraries in JNI
  • Firestore: Version history of documents
  • Implementation of timeout in LDAP
  • flex tree gets chopped even after using scroll bar
  • Why my AngularJS async test in Jasmine 1.3.x is not working?
  • Capture SIGFPE from SIMD instruction
  • Angular 4: Responsive Grid List
  • Cross compile glibc for arm, got undefined reference to some unwind functions
  • How to use FirstOrDefault inside Include
  • How do I use TagLib-Sharp to write custom (PRIV) ID3 frames?
  • WPF custom control and direct content support
  • CAS 4 - Not able to retrieve the LDAP groups after successful authentication
  • JavaScript RegExp Replace