如何清空LiveData储值?

How to clear LiveData stored value?

根据LiveData documentation

The LiveData class provides the following advantages:

...

Always up to date data: If a Lifecycle starts again (like an activity going back to started state from the back stack) it receives the latest location data (if it didn’t already).

但有时我不需要这个功能。

例如,我在 ViewModel 中关注 LiveData,在 Activity 中关注观察者:

//LiveData
val showDialogLiveData = MutableLiveData<String>()

//Activity
viewModel.showMessageLiveData.observe(this, android.arch.lifecycle.Observer { message ->
        AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK") { _, _ -> }
                .show()
    })

现在每次旋转后都会出现旧对话框。

有没有一种方法可以在处理后清除存储值,或者根本就是 LiveData 的错误使用?

更新

实际上有几种方法可以解决这个问题。在文章 LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case) 中对它们进行了很好的总结。这是一位与架构组件团队合作的 Google 员工撰写的。

TL;DR 更稳健的方法是使用 Event wrapper class, which you can see an example of at the bottom of the article.

此模式已进入大量 Android 个示例,例如:

为什么 Event wrapper 优于 SingleLiveEvent?

SingleLiveEvent 的一个问题是,如果一个 SingleLiveEvent 有多个观察者,当数据发生变化时,只有其中一个会收到通知 - 这可能会引入细微的错误并且很难解决。

使用事件包装器 class,您的所有观察者都将正常收到通知。然后,您可以选择显式 "handle" 内容(内容只有 "handled" 一次)或查看内容,无论最新的 "content" 是什么,内容总是 returns。在对话示例中,这意味着您始终可以使用 peek 查看最后一条消息的内容,但请确保对于每条新消息,对话仅触发一次,使用 getContentIfNotHandled.

旧回复

亚历克斯在评论中的回应是我认为正是你要找的东西。有一个名为 SingleLiveEvent 的 class 的示例代码。这个class的目的描述为:

A lifecycle-aware observable that sends only new updates after subscription, used for events like navigation and Snackbar messages.

This avoids a common problem with events: on configuration change (like rotation) an update can be emitted if the observer is active. This LiveData only calls the observable if there's an explicit call to setValue() or call().

我不确定它是否适用于你的情况,但在我的情况下(increasing/decreasing 房间中的项目数量通过点击视图)删除观察者并检查是否有活跃的观察者让我做工作:

LiveData<MenuItem> menuitem = mViewModel.getMenuItemById(menuid);
menuitem.observe(this, (MenuItem menuItemRoom) ->{
                menuitem.removeObservers(this);
                if(menuitem.hasObservers())return;

                // Do your single job here

                });
});  

2019 年 3 月更新:

现在我更喜欢这个: EventWraper class 来自 Google MutableLiveData

内的样本
/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
public class Event<T> {

    private T mContent;

    private boolean hasBeenHandled = false;


    public Event( T content) {
        if (content == null) {
            throw new IllegalArgumentException("null values in Event are not allowed.");
        }
        mContent = content;
    }

    @Nullable
    public T getContentIfNotHandled() {
        if (hasBeenHandled) {
            return null;
        } else {
            hasBeenHandled = true;
            return mContent;
        }
    }

    public boolean hasBeenHandled() {
        return hasBeenHandled;
    }
}

在 ViewModel 中:

 /** expose Save LiveData Event */
 public void newSaveEvent() {
    saveEvent.setValue(new Event<>(true));
 }

 private final MutableLiveData<Event<Boolean>> saveEvent = new MutableLiveData<>();

 LiveData<Event<Boolean>> onSaveEvent() {
    return saveEvent;
 }

在Activity/Fragment

mViewModel
    .onSaveEvent()
    .observe(
        getViewLifecycleOwner(),
        booleanEvent -> {
          if (booleanEvent != null)
            final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
            if (shouldSave != null && shouldSave) saveData();
          }
        });

就我而言,SingleLiveEvent 没有帮助。我使用此代码:

private MutableLiveData<Boolean> someLiveData;
private final Observer<Boolean> someObserver = new Observer<Boolean>() {
    @Override
    public void onChanged(@Nullable Boolean aBoolean) {
        if (aBoolean != null) {
            // doing work
            ...

            // reset LiveData value  
            someLiveData.postValue(null);
        }
    }
};

这种情况下您需要使用 SingleLiveEvent

class SingleLiveEvent<T> : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }

        // Observe the internal MutableLiveData
        super.observe(owner, Observer<T> { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }

    companion object {
        private const val TAG = "SingleLiveEvent"
    }
}

在你的视图模型中 class 创建如下对象:

 val snackbarMessage = SingleLiveEvent<Int>()

如果您需要简单的解决方案,请尝试这个:

class SingleLiveData<T> : MutableLiveData<T?>() {

    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) {
        super.observe(owner, Observer { t ->
            if (t != null) {
                observer.onChanged(t)
                postValue(null)
            }
        })
    }
}

像普通的 MutableLiveData 一样使用它

我找到的最佳解决方案是 live event library 如果您有多个观察者,它会完美运行:

class LiveEventViewModel : ViewModel() {
    private val clickedState = LiveEvent<String>()
    val state: LiveData<String> = clickedState

    fun clicked() {
        clickedState.value = ...
    }
}

可能是一个丑陋的 hack 但是...注意:它需要 RxJava

menuRepository
            .getMenuTypeAndMenuEntity(menuId)
            .flatMap { Single.fromCallable { menuTypeAndId.postValue(Pair(it.first, menuId)) } }
            .flatMap { Single.timer(200, TimeUnit.MILLISECONDS) }
            .subscribe(
                { menuTypeAndId.postValue(null) },
                { Log.d(MenuViewModel.TAG, "onError: ${it.printStackTrace()}") }
            )

我就这样解决了。当没有观察者时,实时数据将自行清除

class SelfCleaningLiveData<T> : MutableLiveData<T>(){
    override fun onInactive() {
       super.onInactive()
       value = null
    }

}