A Custom LiveData that has onActive and onInactive Listeners -- Tested
Tony Robalik
Posted on April 27, 2018
I was expecting my next post to continue my series on dagger.android
, but today's adventures were too strange not to relate to others.
Need: a LiveData
object that would "do something" when it had an active observer.
Observation: LiveData
has two hooks: onActive()
and onInactive()
that are designed for just this purpose.
Plan: Extend MutableLiveData
with a class named ActiveMutableLiveData
that does something with these hooks.
Further observation: if I'm going to push this to production, I should test it to prove to myself I understand what's going on and that my new class behaves as expected.
ActiveMutableLiveData
/**
* A form of [MutableLiveData][android.arch.lifecycle.MutableLiveData] that
* provides hooks
for for [onActive][android.arch.lifecycle.MutableLiveData.onActive] and
* [onInactive][android.arch.lifecycle.MutableLiveData.onInactive]. Use these to be
* notified when this [LiveData][android.arch.lifecycle.LiveData] gains and loses an
* active observer.
*/
class ActiveMutableLiveData<T>(
private val onActiveListener: OnActiveListener
) : MutableLiveData<T>() {
constructor(onActive: () -> Unit = {}, onInactive: () -> Unit = {}) : this(
onActiveListenerFromLambdas(onActive, onInactive)
)
override fun onActive() {
onActiveListener.onActive()
}
override fun onInactive() {
onActiveListener.onInactive()
}
}
fun onActiveListenerFromLambdas(
onActive: () -> Unit,
onInactive: () -> Unit
): OnActiveListener {
return object : OnActiveListener {
override fun onActive() {
onActive.invoke()
}
override fun onInactive() {
onInactive.invoke()
}
}
}
interface OnActiveListener {
fun onActive()
fun onInactive()
}
ActiveMutableLiveDataTest
// Using JUnit5
internal class ActiveMutableLiveDataTest {
private val counter = AtomicInteger()
private val liveData = ActiveMutableLiveData<Boolean>(onActive = {
counter.incrementAndGet()
})
@Test fun whenObjectBecomesActive_thenOnActiveListenerIsTriggered() {
// Given
val lifecycle = TestLifecycle()
liveData.observe(lifecycle, Observer {
println("$it")
})
// verify initial condition
assertEquals(0, counter.get())
// When
lifecycle.state = Lifecycle.State.STARTED
// Then
assertEquals(1, counter.get())
}
@Test fun expectOnActiveListenerTriggeredEveryTime() {
// Given
val lifecycle = TestLifecycle()
liveData.observe(lifecycle, Observer {
println("$it")
})
// verify initial condition
assertEquals(0, counter.get())
// When
lifecycle.state = Lifecycle.State.STARTED
lifecycle.state = Lifecycle.State.CREATED
lifecycle.state = Lifecycle.State.STARTED
// Then
assertEquals(2, counter.get())
}
class TestLifecycle : LifecycleOwner {
var state: Lifecycle.State = Lifecycle.State.INITIALIZED
set(value) {
registry.markState(value)
}
private val lifecycle = object : Lifecycle() {
override fun addObserver(observer: LifecycleObserver) {
// don't care
}
override fun removeObserver(observer: LifecycleObserver) {
// don't care
}
override fun getCurrentState(): State = state
}
private val registry = LifecycleRegistry({ lifecycle })
override fun getLifecycle() = registry
}
}
💖 💪 🙅 🚩
Tony Robalik
Posted on April 27, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.