我最近看到一个奇怪的问题,它是我项目的障碍。 设置实时数据值的多次调用不会调用视图中的观察者。
似乎只有最后设置的值实际上会在视图中调用Observer。
以下是审核的代码段。
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)
}
}
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()
}
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。
答案 0 :(得分:3)
我做了一些科学研究,重新实现了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);
}
}
答案 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)
}
}
}