【问题标题】:How to clear LiveData stored value?如何清除 LiveData 存储值?
【发布时间】: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?

【问题讨论】:

  • 是否与实时数据问题有关?无论您是否使用 LiveData,每次旋转时都会重新创建活动。即使您将其删除,问题也会继续存在。
  • @LongRanger 可以通过在显示对话框后删除缓存在 LiveData 中的消息来解决,因此新活动将不会收到它。与 Moxy 的 OneExecutionStateStrategy 使用的原理相同

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


【解决方案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 示例中,例如:

    为什么事件包装器比 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) } ``
          猜你喜欢
          • 1970-01-01
          • 2020-12-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-09
          • 2012-04-12
          相关资源
          最近更新 更多