Open In App

How to Observe Event Only Once Using SingleLiveEvent in Android?

Improve
Improve
Like Article
Like
Save
Share
Report

Have you ever had to deal with a Dialog or a SnackBar that, after being triggered/shown or dismissed, gets triggered/shown again after a device rotation? This is most likely connected to the use of LiveData observables to communicate between the ViewModel and the Activity/Fragment.

The Pattern’s Opposite

Using LiveData objects for events might be viewed as a poor design choice, especially when the event is only required once. We can use some boolean flags to assist the view in determining if the Dialog/SnackBar should be triggered/shown or not. However, this might result in a solution that is difficult to comprehend and maintain, as well as being unattractive. As you can see it has the type of live data, the data is consumed continuously, but we only want to get it once.

There are no memory leaks:

Observers are tied to Lifecycle objects and clean up after themselves when the lifecycle with which they are connected is destroyed.

There have been no accidents as a result of activity being halted:

If the observer’s lifetime is idle, like in the case of a back stack activity, it does not receive any LiveData events. LiveData just follows the observer design, and I don’t believe the above-mentioned use case warrants its own variant of the observable class. So, how can we resolve the dialogue issue? Rather than altering the behavior of LiveData, we should keep to the basics of the MVVM paradigm and, if necessary, have the ViewModel tell the ViewModel about the dismissed dialogue. Following that, the VM might reset or alter the status of the event. I realize it’s easy to forget to alert ViewModel, and we’re increasing View’s responsibilities. But, after all, it is the View’s job: to inform the VM of any user action.

How to Achieve This?

Create an ‘Event’ class that is general (maybe call it LiveEvent). Give it observe(owner, observer) and observeForever functions (observer). Give it the same contract and functionality as LiveData. Except that, instead of a persistent value, the LiveEvent just notifies active observers while sending along with the data.

Kotlin




class LiveEvent<C> {
...
  fun sendingValue(data: C) {
    observers.forEach { gfg ->
      if (gfg.isActive()) gfg.onChanged(data)
    }
  }
}


Put the LiveEvent in your basic ViewModel class if you want to utilize it for publishing notifications, SnackBars, or anything on many screens. Then, after initializing the ViewModel in your basic Fragment class, watch for that LiveEvent.

Kotlin




abstract class GeeksforGeeksViewModel : ViewModel() {
  protected val fireAlertedEvent = LiveEvent<FireAlert>()
}
abstract class BaseFragment : Fragment() {
  fun onViewModelInitialized() {
    viewModel.fireAlertedEvent.observe(this, { fireAlert ->
      fireAlert.show()
    }
  }
}
class GeeksforGeeksSample : GeeksforGeeksViewModel() {
  fun onSaveSuccess() {
    fireAlertedEvent.postValue(FireAlert("yay! we got your stuff!"))
  }
}


Conclusion

The optimum solution would be an event source with automatic unsubscription on destruct that enqueues events as long as no watchers are present and then sends all events to the first observer that subscribes. That can handle the events in whatever way it deems fit. When you need to manage flow, using a callback with distinct success/failure methods is a terrible idea. Make use of an ADT (sealed class).



Last Updated : 08 Sep, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads