3

I have a RecyclerView with data from an API. After pressing a item from RecyclerView opens a new fragment with another API call where i fetch new data based of ID from item pressed.

When i press first element everything is fine - OBSERVER send me the object from API call, and trigger once - Ok. The problem is when i press for a random second element from Recycler because OBSERVER first will trigger with the object from previous call after will trigger with the right object.

One of the solution is to place an IF and to listen only second trigger but in this case will not work the case when i press for the first time because it will trigger only once and i will wait for second one.

Do you know how to do that observer will trigger only once or will trigger every time twice ?

Dinesh
  • 1,410
  • 2
  • 16
  • 29
Vasile
  • 313
  • 2
  • 7

3 Answers3

1

I found a good answer on Why LiveData observer is being triggered twice for a newly attached observer

In the article above are 2 options-solutions, for me worked only that one when you subscribe for observer one more time. So in my case i moved API call from new fragment to recyclerview fragment and have done 2 subscriptions 1 in Recyclerview and one in new fragment. Now it is triggering 2 times in Recyclerview fragment and 1 time in new Fragment.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Vasile
  • 313
  • 2
  • 7
0

Make sure you create your liveData only once, and make sure you subscribe to it only once (and not per every press)

Alex Timonin
  • 1,872
  • 18
  • 31
  • ViewModel is only once created - from debugger i see this. – Vasile May 19 '20 at 09:22
  • I observe by - observe(getViewLifecycleOwner().... -> this mean that i subscribe once, i think :). In case i subscibe more times, i think this should be like - 1 item i open will tigger once , 2- twice , 3 - 3times .... . But my case is first time once and after 2 times. – Vasile May 19 '20 at 09:29
  • What matters is what lifecycle method this `observe` happens in. – EpicPandaForce May 19 '20 at 15:07
0

Because LiveData will trigger again upon configuration change/screen rotation. We want to handle UI events such as click once only.

Here comes the concept of SingleLiveEvent. It is nothing but an extension of the MutableLiveData class but it emits the data only once whenever required.

We need to create a class file called SingleLiveEvent in our project and the SingleLiveEvent class looks like this,

class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
    if (hasActiveObservers()) {
        Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
    }
    // Observe the internal MutableLiveData
    super.observe(owner, object : Observer<T> {
        override fun onChanged(t: T?) {
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        }
    })
}
@MainThread
override fun setValue(t: T?) {
    mPending.set(true)
    super.setValue(t)
}
/**
 * Used for cases where T is Void, to make calls cleaner.
 */
@MainThread
fun call() {
    setValue(null)
}
companion object {
    private val TAG = "SingleLiveEvent"
}}

and to use it in ViewModel you just need to use it similarly how we use LiveData,

  private val uploadData = SingleLiveEvent<String>()
fun getUploadData(): SingleLiveEvent<String> {
    return uploadData
}

and in the Activity/Fragment file, we will use similarly how we used in the above code,

viewModel.getUploadData().observe(this, Observer {
    //Snackbar
})

Note: Here is the reference link Check the source here