Skip to content

Instantly share code, notes, and snippets.

@kobeumut
Created April 21, 2019 08:24
Show Gist options
  • Save kobeumut/edb3edd9a2ae9abf6984a42bb2de0441 to your computer and use it in GitHub Desktop.
Save kobeumut/edb3edd9a2ae9abf6984a42bb2de0441 to your computer and use it in GitHub Desktop.
Android Livedata Observe Once Only (Kotlin)
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
//Using
liveData.observeOnce(this, Observer<Password> {
if (it != null) {
// do something
}
})
val liveData = viewModel.showSnackBar("Hi everyone")
liveData.observe(this, object: Observer<String> {
override fun onChanged(text: String?) {
liveData.removeObserver(this)
}
})
@DavidJH2
Copy link

I have had good use of this pattern however in one of my use cases when I set up the observer of an existing value, I get an immediate call back before the observed value has changed. My understanding is that it only works if the initial value starts as null. Is this correct and if so what is the solution if the value has a non-null value?

Thanks!

@DavidJH2
Copy link

DavidJH2 commented Jul 26, 2021

Just created this LiveData extension function:

fun <T> LiveData<T>.observeOnceAfterInit(owner: LifecycleOwner, observer: (T) -> Unit) {
	var firstObservation = true
	
	observe(owner, object: Observer<T>
	{
		override fun onChanged(value: T) {
			if(firstObservation)
			{
				firstObservation = false
			}
			else
			{
				removeObserver(this)
				observer(value)
			}
		}
	})
}

Would this be the best way to ignore the initial call back after observer setup

@kobeumut
Copy link
Author

kobeumut commented Aug 1, 2021

Hello David, I'm not sure what is the best solution for it but you are right. And also your script that wrote above is going to work properly.

@DavidJH2
Copy link

DavidJH2 commented Aug 1, 2021

Great, thanks for the reply!

@mohamedmohamedtaha
Copy link

Good Solution David, that Only work for me. All other solution not work for me and I didn't know why.

@badrnezhad
Copy link

@kobeumut Thank you for this useful code

@JeelPatel231
Copy link

thanks for the solution, however for non-nullable variables with initial value, or for patterns with removing observer on certain conditions, this is helpful

fun <T> LiveData<T>.observeUntil(
    owner: LifecycleOwner,
    predicate: (T) -> Boolean,
    observer: (T) -> Unit
) {
    observe(owner, object: Observer<T> {
        override fun onChanged(value: T) {
            if(predicate(value)) {
                removeObserver(this)
            }
            observer(value)
        }
    })
}

this can be used as:

val mutLiveData = MutableLiveData(0)

mutLiveData.observeUntil(viewLifecycleOwner, { it == 10}) {
    /* code goes here... */
}

@DavidJH2
Copy link

DavidJH2 commented Jun 2, 2023

Nice!

@hitesh-dhamshaniya
Copy link

I have implemented that but when the device configuration changes get the result again. what could we do in that case?

set flag on config change will cost a lot as I have implemented that in many places so, I am looking for optimal solutions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment