Множественных вызовов для установки LiveData не наблюдается

Недавно я увидел странную проблему, которая мешает моему проекту. Множественные вызовы для установки значения оперативных данных не вызывают наблюдателя в представлении.

Кажется, что только последнее установленное значение фактически вызывает Observer в представлении.

Вот фрагмент кода для обзора.

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProviders.of(this).get(MainViewModelImpl::class.java)

        viewModel.state().observe(this, Observer {
            onStateChange(it!!)
        })

        viewModel.fetchFirstThree()

    }

    private fun onStateChange(state: MainViewModel.State) {

        when (state) {
            is One -> {
                show(state.data)
            }
            is Two -> {
                show(state.data)
            }
            is Three -> {
                show(state.data)
            }
        }
    }

    private fun show(data: String) {
        Log.d("Response", data)
    }
}

MainViewModel.kt

abstract class MainViewModel : ViewModel() {

    sealed class State {
        data class One(val data: String) : State()
        data class Two(val data: String) : State()
        data class Three(val data: String) : State()
    }

    abstract fun state(): LiveData<State>

    abstract fun fetchFirstThree()
}

MainViewModelImpl.kt

class MainViewModelImpl : MainViewModel() {

    private val stateLiveData: MediatorLiveData<State> = MediatorLiveData()

    override fun state(): LiveData<State> = stateLiveData

    override fun fetchFirstThree() {
        stateLiveData.value = State.One("One")
        stateLiveData.value = State.Two("Two")
        stateLiveData.value = State.Three("Three")
    }
}

Ожидаемый результат:

Response: One
Response: Two
Response: Three

Фактический выход:

Response: Three

Согласно выходным данным выше, Observer не вызывается для первых двух значений.


person mhtmalpani    schedule 29.05.2018    source источник
comment
Попробуйте использовать MutableLiveData ‹State› вместо MediatorLiveData   -  person Aseem Sharma    schedule 29.05.2018
comment
Тот же результат. Результат по-прежнему Ответ: три   -  person mhtmalpani    schedule 29.05.2018


Ответы (5)


Я немного поработал, повторно реализовав LiveData и MutableLiveData, чтобы вывести некоторые данные из системы.

Проверьте исходный код здесь.

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

Похоже, что наблюдатель не достиг активного состояния, когда вы отправляете начальные значения.

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;
    }

Как только наблюдатель достигает активного состояния, он отправляет последнее установленное значение.

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);
    }
}
person Knossos    schedule 29.05.2018
comment
Как в таком случае изменить наш код, чтобы активировать наблюдателя и получать все выданные значения, а не только последнее? - person mhtmalpani; 29.05.2018
comment
Вы пробовали запускать код в onStart () или onResume ()? - person Knossos; 29.05.2018
comment
Если мы вызовем viewModel.fetchFirstThree () в onStart (), он все равно будет отслеживать последнее обновленное значение. Однако, если мы сделаем то же самое в onResume (), он будет вызван для всех трех значений, как мы и ожидали. - person mhtmalpani; 29.05.2018
comment
Вот ты где. Похоже, это случай изменения состояния: LiveData considers an observer, which is represented by the Observer class, to be in an active state if its lifecycle is in the STARTED or RESUMED state. - person Knossos; 29.05.2018
comment
Спас мой день. Спасибо - person Євген Гарастович; 27.02.2019
comment
Если вы хотите сохранить все переданные значения, а не только последнее, используйте настраиваемый класс с учетом жизненного цикла, а не LiveData. - person EpicPandaForce; 10.06.2020

У меня тоже была такая проблема.

Чтобы решить эту проблему, был создан пользовательский MutableLiveData, который содержит очередь опубликованных значений и будет уведомлять наблюдателя для каждого значения.

Вы можете использовать его так же, как и обычный 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, { 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() {
        value = 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
    }
}
person Ondinnonk    schedule 04.09.2020

Вы можете использовать пользовательские LiveData следующим образом:

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)
      }
  }
}
person Dmitry Baryshev    schedule 10.10.2018

FWIW У меня была такая же проблема, но я решил ее вот так ...

Изначально у меня был код, похожий на этот ...

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

updateMonth(1)
updateMonth(2)
updateMonth(3)

У меня возникла та же проблема, что описана ... Но когда я сделал это простое изменение ....

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

Предположительно, каждый updateMonth теперь переходит в другой поток, поэтому все обновления наблюдаются.

person Mark Sheekey    schedule 07.07.2020

Вы должны вызвать viewModel.fetchFirstThree() после метода onStart() Activity. например в onResume() методе.

Потому что в LiveData Observer обернут как LifecycleBoundObserver. Поле mActive устанавливается в истинное значение после 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
    }
}

Когда наблюдатель уведомляет об изменении, он вызывает considerNotify, до onStart он вернется на !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);
}
person Davion    schedule 10.10.2018