如何清除 LiveData 存储值?
Posted
技术标签:
【中文标题】如何清除 LiveData 存储值?【英文标题】:How to clear LiveData stored value? 【发布时间】:2017-11-18 19:18:25 【问题描述】:根据LiveData documentation:
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?
【问题讨论】:
这与:***.com/questions/44146081/… 是否与实时数据问题有关?无论您是否使用 LiveData,每次旋转时都会重新创建活动。即使您将其删除,问题也会继续存在。 @LongRanger 可以通过在显示对话框后删除缓存在 LiveData 中的消息来解决,因此新活动将不会收到它。与 Moxy 的 OneExecutionStateStrategy 使用的原理相同 【参考方案1】:可能是一个丑陋的黑客,但...注意:它需要 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()")
)
【讨论】:
【参考方案2】:我找到的最佳解决方案是live event library,如果您有多个观察者,它会非常有效:
class LiveEventViewModel : ViewModel()
private val clickedState = LiveEvent<String>()
val state: LiveData<String> = clickedState
fun clicked()
clickedState.value = ...
【讨论】:
使用这个库要小心的一件事是它不能像标准 LiveData 那样向新观察者发出最新值。否则我喜欢它。【参考方案3】:更新
实际上有几种方法可以解决这个问题。文章LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case) 很好地总结了它们。这是由与架构组件团队合作的一位 Google 员工编写的。
TL;DR 更可靠的方法是使用Event wrapper class,您可以在the article 的底部看到一个示例。
这种模式已经应用到许多 Android 示例中,例如:
Plaid Architecture Blueprints iosched为什么事件包装器比 SingleLiveEvent 更受欢迎?
SingleLiveEvent 的一个问题是,如果 SingleLiveEvent 有多个观察者,则当数据发生更改时,只有其中一个会收到通知 - 这可能会引入细微的错误并且很难解决。
使用事件包装类,您的所有观察者都将正常收到通知。然后,您可以选择显式“处理”内容(内容仅“处理”一次)或查看内容,该内容始终返回最新的“内容”。在对话框示例中,这意味着您始终可以使用peek
查看最后一条消息是什么,但请确保对于每条新消息,只触发一次对话框,使用getContentIfNotHandled
。
旧响应
Alex 在 cmets 中的回答是我认为正是您想要的。有一个名为SingleLiveEvent 的类的示例代码。此类的目的描述为:
一个生命周期感知的可观察对象,在之后只发送新的更新 订阅,用于导航和 Snackbar 消息等事件。
这避免了事件的一个常见问题:配置更改 (如旋转)如果观察者处于活动状态,则可以发出更新。 此 LiveData 仅在有明确调用时才调用 observable setValue() 或 call()。
【讨论】:
【参考方案4】:我不确定它是否适用于您的情况,但在我的情况下(通过点击视图增加/减少房间中的项目数量)删除观察者并检查是否有活跃的观察者让我完成这项工作:
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;
在 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;
在活动/片段中
mViewModel
.onSaveEvent()
.observe(
getViewLifecycleOwner(),
booleanEvent ->
if (booleanEvent != null)
final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
if (shouldSave != null && shouldSave) saveData();
);
【讨论】:
【参考方案5】:如果你需要简单的解决方案,试试这个:
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 一样使用它
【讨论】:
【参考方案6】:这种情况你需要使用 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"
在你的 viewmodel 类里面创建对象,比如:
val snackbarMessage = SingleLiveEvent<Int>()
【讨论】:
我用过这个类,但是它又触发了【参考方案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);
;
【讨论】:
显然只是猜测,但这可能对您的情况没有帮助,因为您使用的是postValue
,而示例 SingleLiveEvent 实现不满足这一点。不过很容易修复,添加 - ``` override fun postValue(value: T) pending.set(true) super.postValue(value) ``以上是关于如何清除 LiveData 存储值?的主要内容,如果未能解决你的问题,请参考以下文章
Room:来自 Dao 的 LiveData 将在每次更新时触发 Observer.onChanged,即使 LiveData 值没有变化
对 Android 的 LiveData 网传的数据倒灌做一个深层次的解释
对 Android 的 LiveData 网传的数据倒灌做一个深层次的解释