【问题标题】:Is livedata builder ok for one-shot operations?livedata builder可以进行一次性操作吗?
【发布时间】:2020-12-26 11:33:43
【问题描述】:

例如,假设我们有一个产品目录视图,其中包含将产品添加到购物车的选项。 每次用户点击添加到购物车时,都会调用一个 viewModel 方法 addToCart,如下所示:

//inside viewModel
fun addToCart(item:Item): LiveData<Result> = liveData {
    val result = repository.addToCart(item) // loadUser is a suspend function.
    emit(result)
}


//inside view
addButton.onClickListener = {
     viewModel.addToCart(selectedItem).observe (viewLifecycleOwner, Observer () {
          result -> //show result
    }
}

添加例如 5 个项目后会发生什么 -> 视图观察到的内存中会有 5 个 livedata 对象吗?

如果是,什么时候清理?如果是,我们是否应该避免使用 livedata builder 进行可以多次调用的一次性操作?

【问题讨论】:

    标签: android kotlin mvvm android-livedata


    【解决方案1】:

    您的实现似乎错误!您不断地为每个 addToCard 函数调用返回一个新的 LiveData 对象。关于你的第一个问题,是Yes

    如果你想通过 liveData 正确地做到这一点。

    // In ViewModel
    
    private val _result = MutableLiveData<Result>()
    val result: LiveData<Result>
       get() = _result;
    
    fun addToCart(item: Item) {
       viewModelScope.launch {
          // Call suspend functions
          result.value = ...
       }
    }
    
    // Activity/Fragment
    
    viewModel.result.observe(lifecycleOwner) { result ->
       // Process the result  
       ...
    }
    
    viewModel.addToCart(selectedItem)
    

    您所要做的就是从活动中调用它并处理结果。您也可以为此目的使用StateFlow。它还有一个扩展 asLiveData,它也可以转换 Flow -> LiveData。

    【讨论】:

    • 您的答案指向“正确”的方式,但您没有回答为什么它是错误的以及“当不断返回新的 LiveData 时会发生什么”的问题。
    • 根据 OP 的代码,它只会在单击按钮时调用观察者一次,后续调用将创建它的新实例并只调用一次观察者。最终,那些以前的LiveData 对象应该会自动被垃圾回收。
    • @kaustubhpatange 谢谢,你的回答正是我目前正在做的事情 -> 但是,我问了这个问题,因为我在多个地方发现 MutableLiveData 可以甚至应该是替换为新的 livedata builder 块,这是我对此的担忧 - 看起来对于可重复的一次性操作不安全。
    • “可重复的一次性”= 悖论!如果您想要一个一次性的 LiveData,这通常是导航事件的情况,那么每次订阅者想要订阅时实例化实时数据是一种非常简单的实现方式。这显然只适用于需要单个订阅者的情况。否则,即重用实例,会产生 SingleLiveEvent 打算解决的问题。为什么要制造额外的问题?
    【解决方案2】:

    根据LiveData实现:

        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            assertMainThread("observe");
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                // ignore
                return;
            }
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    

    每次观察 LiveData 时都会添加一个新的观察者 (wrapper)。看着这个,我会小心地从视图(点击)事件中创建新的观察者。目前我不知道垃圾收集器是否可以释放这些资源。

    正如@kaustubhpatange 所提到的,您应该拥有一个 LiveData,其状态/值可以由 viewModel 更改,以及每个新结果。 LiveData 可以在您的 Activity 或 Fragment onCreate() 函数中观察到(一次):

    fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
    
      viewModel.result.observe(lifecycleOwner) { result ->
        // handle the result
      }
    }
    

    在 ViewModel 中使用 MutableLiveData,您通常只能创建一次 LiveData,然后使用来自点击事件、响应等的值填充它。

    【讨论】:

    • 谢谢,这就是我目前的做法...我试图评估是否应该切换到 livedata { } 构建器,但您刚刚证实了我对多个实例的担忧。
    【解决方案3】:

    TL;DR

    如果您的操作是 One-Shot,请使用 CoroutineLiveData

    如果您的操作使用Streams,您可以使用Flow

    对于一次性操作,您的方法没问题。 我认为liveData builder 没有任何内存泄漏。 例如,如果您使用 LiveData 的私有支持属性并观察公共 LiveData 它可能会发生不同的行为,例如在为其分配新值之前获取最新值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 2011-05-27
      • 2013-07-02
      • 2012-07-31
      相关资源
      最近更新 更多