72

根据LiveData 文档

LiveData 类具有以下优点:

...

始终保持最新数据:如果生命周期再次启动(例如活动从后台堆栈返回到启动状态),它会接收最新的位置数据(如果还没有)。

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

例如,我在 ViewModel 中有以下 LiveData,在 Activity 中有 Observer:

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

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

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

有没有办法在处理后清除存储的值,或者它是否完全错误地使用了 LiveData?

4

7 回答 7

69

更新

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

TL;DR一种更可靠的方法是使用Event wrapper 类,您可以在文章底部看到一个示例。

这种模式已经应用到许多 Android 示例中,例如:

为什么事件包装器比 SingleLiveEvent 更受欢迎?

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

使用事件包装器类,您的所有观察者都将正常收到通知。然后,您可以选择显式“处理”内容(内容仅“处理”一次)或查看内容,该内容始终返回最新的“内容”。在对话框示例中,这意味着您始终可以看到最后一条消息是什么peek,但要确保对于每条新消息,对话框只触发一次,使用getContentIfNotHandled.

旧回应

亚历克斯在评论中的回应是我认为正是您正在寻找的。有一个名为SingleLiveEvent的类的示例代码。此类的目的描述为:

一个生命周期感知的可观察对象,在订阅后只发送新的更新,用于导航和 Snackbar 消息等事件。

这避免了事件的常见问题:在配置更改(如旋转)时,如果观察者处于活动状态,则可以发出更新。如果显式调用 setValue() 或 call(),则此 LiveData 仅调用 observable。

于 2017-06-30T02:55:13.757 回答
8

我不确定它是否适用于您的情况,但在我的情况下(通过单击视图增加/减少房间中的项目数量)删除观察者并检查是否有活跃的观察者让我完成这项工作:

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 月 20 日更新:

现在我更喜欢这个:来自 Google Samples 中 MutableLiveData 的 EventWraper 类

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

在视图模型中:

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

在活动/片段中

mViewModel
    .onSaveEvent()
    .observe(
        getViewLifecycleOwner(),
        booleanEvent -> {
          if (booleanEvent != null)
            final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
            if (shouldSave != null && shouldSave) saveData();
          }
        });
于 2018-03-30T12:17:14.773 回答
7

在我的情况下 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);
        }
    }
};
于 2018-04-10T18:35:24.703 回答
3

对于这种情况,您需要使用 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"
    }
}

在您的视图模型类中创建对象,例如:

 val snackbarMessage = SingleLiveEvent<Int>()
于 2018-09-05T10:56:38.807 回答
3

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

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 一样使用它

于 2018-10-22T12:34:36.823 回答
0

我找到的最佳解决方案是实时事件库,如果您有多个观察者,它可以完美运行:

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

    fun clicked() {
        clickedState.value = ...
    }
}
于 2019-06-03T09:39:05.887 回答
0

可能是一个丑陋的黑客,但......注意:它需要 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()}") }
            )
于 2020-08-13T20:20:29.883 回答