未观察到多次调用以设置LiveData

mht*_*ani 5 android viewmodel kotlin 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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()
}
Run Code Online (Sandbox Code Playgroud)

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

Response: One
Response: Two
Response: Three
Run Code Online (Sandbox Code Playgroud)

实际输出:

Response: Three
Run Code Online (Sandbox Code Playgroud)

根据上面的输出,前两个值未调用Observer。

Kno*_*sos 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
Run Code Online (Sandbox Code Playgroud)

当您发送初始值时,观察者似乎尚未达到活动状态。

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;
    }
Run Code Online (Sandbox Code Playgroud)

一旦观察者达到活动状态,它就会发送最后一个设置值。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你在这。看起来改变状态的情况是这样的:“如果观察者的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 认为由 Observer 类表示的观察者处于活动状态。” (2认同)

小智 5

您可以像这样使用自定义 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)
      }
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我也有这样的问题。

为了解决这个问题,我们创建了自定义 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
    }
}
Run Code Online (Sandbox Code Playgroud)