A Custom LiveData that has onActive and onInactive Listeners -- Tested
Tags: livedata, android, kotlin, testing
Published: 2018-04-27 23:14:56

---
title: A Custom LiveData that has onActive and onInactive Listeners -- Tested
published: true
description: Customizing Android's Architectural Components, and testing them
tags: livedata, android, kotlin, testing
---

I was expecting my next post to continue my [series](https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj) 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`

```kotlin
/**
 * 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`

```kotlin
// 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
    }
}

```