如何清除 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 网传的数据倒灌做一个深层次的解释

对 Android 的 LiveData 网传的数据倒灌做一个深层次的解释

无法在 LiveData 中设置值

如何将editText值传递给viewModel和Livedata(Kotlin)