Multiple calls to set LiveData is not observed

后端 未结 5 1042
闹比i
闹比i 2021-01-11 18:28

I have recently seen a weird issue that is acting as a barrier to my project. Multiple calls to set the live data value does not invoke the observer in the view.

It

相关标签:
5条回答
  • 2021-01-11 18:46

    FWIW I had the same problem but solved it like this...

    I originally had some code similar to this...

    private fun updateMonth(month: Int){
    updateMonth.value = UpdateMonth(month, getDaysOfMonth(month))
    }
    
    updateMonth(1)
    updateMonth(2)
    updateMonth(3)
    

    I experienced the same problem as described... But when I made this simple change....

     private fun updateMonth(month: Int) {
            CoroutineScope(Dispatchers.Main).launch {
                updateMonth.value = UpdateMonth(month, getDaysOfMonth(month))
            }
        }
    

    Presumably, each updateMonth is going onto a different thread now, so all of the updates are observed.

    0 讨论(0)
  • 2021-01-11 18:52

    I did some science, re-implementing LiveData and MutableLiveData to log out some data.

    Check the source code here.

    setValue value=Test1
    dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
    considerNotify
    Returned at !observer.active
    setValue value=Test2
    dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
    considerNotify
    Returned at !observer.active
    setValue value=Test3
    dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
    considerNotify
    Returned at !observer.active
    dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
    considerNotify
    ITEM: Test3
    

    It looks like the observer hasn't reached an active state when you send the initial values.

    private void considerNotify(LifecycleBoundObserver observer) {
        // <-- Three times it fails here. This means that your observer wasn't ready for any of them.
        if (!observer.active) {
            return;
        }
    

    Once the observer reaches an active state, it sends the last set value.

    void activeStateChanged(boolean newActive) {
        if (newActive == active) {
            return;
        }
        active = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += active ? 1 : -1;
        if (wasInactive && active) {
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !active) {
            onInactive();
        }
        if (active) {
            // <--- At this point you are getting a call to your observer!
            dispatchingValue(this);
        }
    }
    
    0 讨论(0)
  • 2021-01-11 18:52

    I had such issue too.

    To resolve it was created custom MutableLiveData, that contains a queue of posted values and will notify observer for each value.

    You can use it the same way as usual MutableLiveData.

    open class MultipleLiveEvent<T> : MutableLiveData<T>() {
    private val mPending =
        AtomicBoolean(false)
    private val values: Queue<T> = LinkedList()
    
    @MainThread
    override fun observe(
        owner: LifecycleOwner,
        observer: Observer<in T>
    ) {
        if (hasActiveObservers()) {
            Log.w(
                this::class.java.name,
                "Multiple observers registered but only one will be notified of changes."
            )
        }
        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t: T ->
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
                //call next value processing if have such
                if (values.isNotEmpty())
                    pollValue()
            }
        })
    }
    
    override fun postValue(value: T) {
        values.add(value)
        pollValue()
    }
    
    private fun pollValue() {
        setValue(values.poll())
    }
    
    @MainThread
    override fun setValue(t: T?) {
        mPending.set(true)
        super.setValue(t)
    }
    
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @Suppress("unused")
    @MainThread
    fun call() {
        value = null
    }
    

    }

    0 讨论(0)
  • 2021-01-11 19:06

    You could use custom LiveData like this:

    class ActiveMutableLiveData<T> : MutableLiveData<T>() {
    
      private val values: Queue<T> = LinkedList()
    
      private var isActive: Boolean = false
    
      override fun onActive() {
          isActive = true
          while (values.isNotEmpty()) {
              setValue(values.poll())
          }
      }
    
      override fun onInactive() {
          isActive = false
      }
    
      override fun setValue(value: T) {
          if (isActive) {
              super.setValue(value)
          } else {
              values.add(value)
          }
      }
    }
    
    0 讨论(0)
  • 2021-01-11 19:13

    You should call viewModel.fetchFirstThree() after Activity's onStart() method. for example in onResume() method.

    Because in LiveData the Observer is wrapped as a LifecycleBoundObserver. The field mActive set to true after onStart().

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
    
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);// return true after onStart()
        }
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());// after onStart() change mActive to true
        }
    }
    

    When the observer notify the change it calls considerNotify, before onStart it will return at !observer.mActive

     private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {// called in onCreate() will return here.
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
    
    0 讨论(0)
提交回复
热议问题