Android: LiveData without repeatable for N > 1 observers

Георгий Чеботарёв
2 min readFeb 17, 2021

Live-data is a powerful instrument, but time to time everybody faced with unwanted behavior of it. Straight to the point. We have a live-data property and method for updating it throw repository:

class MyViewModel : ViewModel {
val updateUi = MutableLiveData<String>()
fun updateNotification() = viewModelScope.launch {
repository.updateNotification().apply {
updateUi.postValue(this)
}
}
}

When request will be executed we will get the result in observer:

// observer
private val updateUiObserver = Observer<String> {
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// send request to server
viewModel.updateNotification()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
// observer subscription
viewModel.updateUi.observe(viewLifecycleOwner, updateUiObserver)
}

All looks fine… But! What if you need to get result only once without repeats after re-observing? The most part of developers will try to clear property by onDestroyView (reset to null or empty string) — not a clear solution. Somebody will use Event (link to articale), but it doesn’t work with 2 or more observers. I suggest another solution:

/**
* Event live data provide similar functionality like liveData except observer recalls moments:
* - value will be sent to all observers one time - it's warranty
* - value will not be resent again after re-observing (for ex. after view recreation)
*
*
@param T Any object
*
@constructor Create empty Event live data
*/
open class EventLiveData<T> : MutableLiveData<T> {
private val observerCounter = AtomicInteger(0)
private val eventCounter = AtomicInteger(0)

constructor() : super()

constructor(value: T) : super(value)

@MainThread
override fun setValue(value: T) {
eventCounter.set(0)
super.setValue(value)
}

@MainThread
override fun postValue(value: T) {
super.postValue(value)
}

@MainThread
fun postValueIfChanged(value: T) {
if (this.value != value)
super.postValue(value)
}

@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) =
super.observe(owner, ObserverWrapper(eventCounter, observerCounter, observer))

@MainThread
override fun observeForever(observer: Observer<in T>) =
super.observeForever(ObserverWrapper(eventCounter, observerCounter, observer))

@MainThread
override fun removeObserver(observer: Observer<in T>) {
observerCounter.decrementAndGet()
super.removeObserver(observer)
}
}

private class ObserverWrapper<T>(
private var eventCounter: AtomicInteger,
private var observerCounter: AtomicInteger,
private val observer: Observer<in T>
) : Observer<T> {

init {
observerCounter.getAndIncrement()
}

private var _observer: Observer<in T> = Observer {
if (eventCounter.get() < observerCounter.get()) {
_observer = observer
_observer.onChanged(it)
}
}

override fun onChanged(value: T) {
_observer.onChanged(value)
eventCounter.getAndIncrement()
}
}

This Live-data extension guarantees one execution without repeating for all observers.

The point is to count the number of subscribers and analyze the number of events: if we have N observers the change function will be called N times. If later the count of observers will change, N will be changed too and new observer will get value.

It’s easy to use - just add class to your project and change live-data to event-live-data:

val updateUi = EventLiveData<String>()

Thanks you for attention!

--

--