How to use SingleLiveEvent in MVVM + Architecture Component.

Abhishek Tiwari
4 min readJun 26, 2018

--

But why should i use it?
Hell, What is SingleLiveEvent anyway?

Let me start by why should we use SingleLiveEvent/LiveEvent in our MVVM app. While finding out about the best practices of communication between activities/fragments and ViewModel, i came across this github issue and i got the best possible solution could’ve found. So basically LiveEvent is used to send events/actions to your fragment/activity from your viewmodel, hence doing away with the use of callbacks.

But what’s wrong with callbacks anyway?

Using callbacks between ViewModel and View makes you to break MVVM so much that now you’re just doing MVP with ViewModel as your presenter, i.e. you’re following MVP and not MVVM, it also causes problem when you want to use your viewmodel with different fragments of same activity, apart from this you will also have to clear your callback assignment on viewmodel’s OnCleared method, which again is a hassle.

How is SingleLiveEvent is better?

If you follow Jose Alcerreca’s blog here, you will know exactly why, in simple terms, SingleLiveEvent is a subclass of MutableLiveData with a single Observer Observing it at a time, hence it is aware of view’s lifecycle.
I have been using it in my projects for sometime and boy has my life been easier! You can find SingleLiveEvent code here.

How to use SingleLiveEvent?

This is how I use SingleLiveEvent in my projects:

  1. Add SingleLiveEvent class in your code: SingleLiveEvent is not yet part of Android Architecture Lifecycle Component library and you’ll have to include it in your code. Make a class name SingleLiveEvent.java and paste this code in it.
  2. After this we will create a sealed class called BaseCommand.kt, you can learn about sealed classes here. In layman terms sealed classes are similar to enums in java and we use it to create different events from same ViewModel.
sealed class BaseCommand {

class Error(val errorString: String): BaseCommand()

class Success(val toastMessage: String?): BaseCommand()

}

3. The above code shows a sealed class in Kotlin which i will use as an inferred type for my SingleLiveEvent. This is to ensure that a single SingleLiveEvent can emit multiple events happening in one view model.

4. The code below shows usage of SingleLiveEvent with the above sealed class from a ViewModel named ApiViewModel.

class ApiViewModel @Inject constructor(private val apiRepository: Api,
private val sharedPrefRepository: SharedPref)
: ViewModel(),
LifecycleObserver {
val command: SingleLiveEvent<BaseCommand> = SingleLiveEvent()
.
.
.
private fun startNetworkRequest(flag: String) {
showProgressBar()
apiRepository.verifyPhone(phone = phone.get()!!,
country = selectedCountryCode!!,
flag = flag)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSuccess {
command.value = BaseCommand.Success("Network connection successfull")
}
.subscribe({
hideProgressBar()
}, { t: Throwable? ->
command.value = BaseCommand.Error("Some error occured")
Timber.d(t?.message)
hideProgressBar()
})
}
.
.
.
}

The above code snippet contains a lot of things, but for the sake of understanding, you need to only focus on these lines:

val command: SingleLiveEvent<BaseCommand> = SingleLiveEvent()

We are initiating our SingleLiveEvent here with BaseCommand as the inferred type, so that we can emit events of the BaseCommand subtype. You can also have an Int, String or any other type but that will restrict you to observe changes in the values of Int or String, using Sealed class here is much more clean and lets you send different type of values too.

command.value = BaseCommand.Success("Network connection successfull")command.value = BaseCommand.Error("Some error occured")

Here we are changing our SingleLiveEvent values this will be observed by the view.

5. Now we will need to observe the changes in SingleLiveEvent from inside our view like this:

apiViewModel.command.observe(this, Observer {
when(it) {
is BaseCommand.Success ->
val snackBar = Snackbar.make(view, it.toastMessage.toString(), Snackbar.LENGTH_INDEFINITE)
snackBar.setAction("Okay", { snackBar.dismiss() })
snackBar.show()
}
is BaseCommand.Error -> {
val snackBar = Snackbar.make(view, it.errorString.toString(), Snackbar.LENGTH_INDEFINITE)
snackBar.setAction("Okay", { snackBar.dismiss() })
snackBar.show()
}
}
}

As seen in the above code, SingleLiveEvent change in values are observed inside when, just like how we do it with LiveData(SingleLiveEvent is subclass of LiveData).

So, this is how you can have communication between your viewmodel and view, without using interface callbacks that are not lifecycle aware.

Note: If you’re reusing your viewmodel in the case of single activity and multiple fragment, where multiple fragments are associated with a single viewmodel, the above use of SingleLiveEvent won’t work. This is because, if you see SingleLiveEvent’s code you’ll find that SingleLiveEvent can be associated with a single observer at a time, so if multiple lifecycle owners(fragments) try to observe the same SingleLiveEvent, the observation will work with only the first observer.

Solution :

  1. In my project, since the complexity is not too much, i simply call removeObserver in my fragment’s onDestroyView().
override fun onDestroyView() {
lifecycle.removeObserver(loginViewModel)
super.onDestroyView()
}

2. You can follow Jose Alcerreca’s blog here, where he talks about this case in detail.

--

--

Abhishek Tiwari

Android developer. Python enthusiast. IoT newbie.Open Source Contributor.Anime buff.