Reputation: 1115
Scenario
Hi, I have an Activity with a ViewPager. In the ViewPagerAdapter, I create instances of a same fragment with different data. And in each instance I initialize a ViewModel
val dataViewModelFactory = this.activity?.let { DataViewModelFactory(it) }
mainViewModel = ViewModelProviders.of(this, dataViewModelFactory).get(MainViewModel::class.java)
In my fragment, I observe two MutableLiveData when I call APIs
mainViewModel.isResponseSuccessful.observe(this, Observer { it ->
if(it) {
//do Something
}else{
Toast.makeText(activity, "Error in Sending Request", Toast.LENGTH_SHORT).show()
}
})
mainViewModel.isLoading.observe(this, Observer {
if (it) {
println("show progress")
} else {
println("dismiss progress")
}
})
In each fragment, on a button click I load another fragment. And if required call and API to fetch data.
PROBLEM
The code comes to the observe block multiple times in my fragment. When I comeback from one fragment to previous fragment, even though no API is called, the code on observe block is executed.
What I tried
I tried using an activity instance in the ViewModel initialization
mainViewModel = ViewModelProviders.of(activity,dataViewModelFactory).get(MainViewModel::class.java)
But it did not work.
Please help,
Upvotes: 1
Views: 2231
Reputation: 1
If you want to prevent multiple calls of your observer just use distinctUntilChanged before observer.
mainViewModel.isResponseSuccessful.distinctUntilChanged().observe(this, Observer { it ->
if(it) {
//do Something
}else{
Toast.makeText(activity, "Error in Sending Request", Toast.LENGTH_SHORT).show()
}
})
mainViewModel.isLoading.distinctUntilChanged().observe(this, Observer {
if (it) {
println("show progress")
} else {
println("dismiss progress")
}
})
Upvotes: 0
Reputation: 22832
It might help you:
import java.util.concurrent.atomic.AtomicBoolean
class OneTimeEvent<T>(
private val value: T
) {
private val isConsumed = AtomicBoolean(false)
private fun getValue(): T? =
if (isConsumed.compareAndSet(false, true)) value
else null
fun consume(block: (T) -> Unit): T? =
getValue()?.also(block)
}
fun <T> T.toOneTimeEvent() =
OneTimeEvent(this)
First, when you want to post a value on LiveData
, use toOneTimeEvent()
extension function to wrap it in a OneTimeEvent
:
liveData.postValue(yourObject.toOneTimeEvent())
Second, when you are observing on the LiveData
, use consume { }
function on the delivered value to gain the feature of OneTimeEvent
. You'll be sure that the block of consume { }
will be executed only once.
viewModel.liveData.observe(this, Observer {
it.consume { yourObject ->
// TODO: do whatever with 'yourObject'
}
})
In this case, when the fragment resumes, your block of code does not execute again.
Upvotes: 2
Reputation: 146
If you want to prevent multiple calls of your observer u can just change MutableLiveData
to SingleLiveEvent
. Read this
Upvotes: 1