'Live data observer triggered twice on fragment created

The issue that I have is not actually bug or big problem. And all works as it should, but nevertheless it annoys me.

In Fragment pbserver:

viewModel.pageNumbersPosition.observe(viewLifecycleOwner) {
        if (it!=null) {
            SharedPreferenceHelper.pagesNumber = viewModel.pageNumbersArray.value?.get(it)
            DLog.d("Set: ${viewModel.pageNumbersArray.value?.get(it)}}")
            //Log shows twice as start
        }
}

ViewModel:

class MenuViewModel : ViewModel() {

    var pageNumbersArray = MutableLiveData(getPageNumbering())
    var pageNumbersPosition = MutableLiveData(pageNumbersArray.value?.indexOf(SharedPreferenceHelper.pagesNumber))

    private fun getPageNumbering():Array<String> {
        val list = mutableListOf<String>()
        for (i in 1..25) {
            list.add(i.toString())
        }
        return list.toTypedArray()

    }
}

Spinner:

 <Spinner
                android:id="@+id/spinner"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:entries="@{viewModel.pageNumbersArray}"
                android:selectedItemPosition="@={viewModel.pageNumbersPosition}"/>

What happes is viewModel.pageNumbersPosition.observe triggered twice on start. Once from the initiation of the fragment and second time when the spinner sets. This is actually suppose to happen, but I don't like it when Shared Preference sets twice.



Solution 1:[1]

I came across a handy class SingleLiveEvent that we can use instead of LiveData in ViewModel class to send only new updates after subscription.

class SingleLiveEvent<T> : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner, Observer<T> { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }

    fun call() {
        postValue(null)
    }
}

This LiveData extension only calls the observable if there's an explicit call to setValue() or call().

Update, primary constructor with parameter:

class SingleLiveEvent<T>(value: T) : MutableLiveData<T>(value) {...}

Solution 2:[2]

You can check if there is equal value in your shared to avoid the double set

if (it!=null) {
    viewModel.pageNumbersArray.value?.get(it).let{ value ->
        if (SharedPreferenceHelper.pagesNumber != value) 
             SharedPreferenceHelper.pagesNumber = value
}

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Eddy Yoel Fresno Hernández