如何清除LiveData存储值?

时间:2017-06-16 06:14:30

标签: android android-architecture-components android-livedata

根据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?

7 个答案:

答案 0 :(得分:32)

更新

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

TL; DR 更强大的方法是使用Event wrapper class,您可以在the article的底部看到一个示例。

这种模式已经进入了许多Android样本,例如:

为什么Event包装器优先于SingleLiveEvent?

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

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

旧回复

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

  

生命周期感知的observable,仅在之后发送新的更新   订阅,用于导航和Snackbar消息等事件。

     

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

答案 1 :(得分:4)

在我的情况下,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);
        }
    }
};

答案 2 :(得分:1)

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

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

                // Do your single job here

                });
});  

更新20/03/2019:

现在我更喜欢这个: MutableLiveData中的Google Samples中的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();
          }
        });

答案 3 :(得分:1)

在这种情况下,您需要使用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>()

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

我发现最好的解决方案是live event library,如果您有多个观察者,它会很好地工作:

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

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

答案 6 :(得分:0)

可能是一个丑陋的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()}") }
            )