未观察到多次调用设置LiveData

时间:2018-05-29 10:58:34

标签: android kotlin viewmodel android-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。

5 个答案:

答案 0 :(得分:3)

我做了一些科学研究,重新实现了LiveData和MutableLiveData来注销一些数据。

检查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

当您发送初始值时,观察者看起来没有达到活动状态。

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

答案 1 :(得分:3)

我也有这样的问题。

为解决此问题,创建了自定义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, 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
}

}

答案 2 :(得分:2)

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现在都进入不同的线程,因此可以观察到所有更新。

答案 3 :(得分:0)

您应该在Activity的viewModel.fetchFirstThree()方法之后调用onStart()。例如onResume()方法。

因为在LiveData中,Observer被包装为LifecycleBoundObserver。在mActive之后,字段onStart()设置为true。

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

答案 4 :(得分:0)

您可以像这样使用自定义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)
      }
  }
}