为啥 LiveData 有一个单独的 MutableLiveData 子类?

Posted

技术标签:

【中文标题】为啥 LiveData 有一个单独的 MutableLiveData 子类?【英文标题】:Why there's a separate MutableLiveData subclass of LiveData?为什么 LiveData 有一个单独的 MutableLiveData 子类? 【发布时间】:2018-03-30 13:30:14 【问题描述】:

看起来MutableLiveDataLiveData 的不同之处仅在于公开了setValue()postValue() 方法,而在LiveData 中它们受到保护。

有什么原因要为此更改创建一个单独的类,而不是简单地将这些方法定义为 LiveData 本身中的公共方法?

一般来说,这种继承形式(增加某些方法的可见性是唯一的变化)是一种众所周知的做法吗?在哪些情况下它可能有用(假设我们可以访问所有代码)?

【问题讨论】:

这是一个设计决策。 LiveData 是不可变的,因为客户端不能改变内部状态,因此是线程安全的 【参考方案1】:

在LiveData - android Developer Documentation 中,您可以看到LiveDatasetValue()postValue() 的方法是不公开的。

而在MutableLiveData - Android Developer Documentation 中,您可以看到MutableLiveData 在内部扩展LiveData,并且LiveData 的两个魔术方法在此公开 可用,它们是@987654329 @&postValue().

setValue():设置值并将值分发给所有活跃的观察者,必须从主线程调用。

postValue() : 将任务发布到主线程以覆盖setValue() 设置的值,必须从后台线程调用。

所以,LiveData不可变的MutableLiveDataLiveData,它是可变的 & 线程安全的

【讨论】:

LiveData 并不是不可变的,只是它不能在 ViewModel 类的外部修改。 ViewModel 类可以根据需要修改它(例如计时器 ViewModel)。如果你想在 ViewModel 类之外修改它,你可以使用 MutableLiveData。 让我们看一下这个场景,一个具有存储库模式(服务器 + 房间)的应用程序,其中房间是唯一的事实来源。 App 只从 Room 获取数据,而 Room 从服务器获取更新。 mutableLiveData 是必须使用的,因为来自服务器的数据更新 Room,还是可以使用 LiveData? LiveData 是抽象的,因此您无法直接创建 LiveData 对象而不对其进行扩展。 MutableLiveData 扩展了 LiveData。 LiveData 和 MutableLiveData 的链接直接指向已弃用的文档。为什么当我建议使用实际链接进行编辑时,它被拒绝了? @Daniel 不确定为什么它被审核队列中的其他审核者拒绝。我已批准更改,谢谢! :)【参考方案2】:

这是整个MutableLiveData.java 文件:

package androidx.lifecycle;
/**
 * @link LiveData which publicly exposes @link #setValue(T) and @link #postValue(T) method.
 *
 * @param <T> The type of data hold by this instance
*/
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> 
    @Override
    public void postValue(T value) 
        super.postValue(value);
    
    @Override
    public void setValue(T value) 
        super.setValue(value);
    

所以是的,只有将 postValuesetValue 设为公开才能有所不同。

我能回忆起的一个用例是在 Kotlin 中使用 Backing Property 进行封装。 您可以将LiveData 公开给您的片段/活动(UI 控制器),即使您可以在您的ViewModel 类中使用MutableLiveData 进行操作。

    class TempViewModel : ViewModel() 
        ...
        private val _count = MutableLiveData<Int>()
        val count: LiveData<Int>
            get() = _count
        public fun incrementCount() = _count.value?.plus(1)
        ...
    

这样,您的 UI 控制器将只能观察值而无法编辑它们。显然,您的 UI 控制器可以使用 TempViewModel 的公共方法编辑值,例如 incrementCount()

注意:澄清可变/不可变混淆 -

data class User(var name: String, var age: Int)

class DemoLiveData: LiveData<User>()

var demoLiveData: LiveData<User>? = DemoLiveData()

fun main() 
    demoLiveData?.value = User("Name", 23) // ERROR
    demoLiveData?.value?.name = "Name" // NO ERROR
    demoLiveData?.value?.age = 23  // NO ERROR

【讨论】:

什么是_score【参考方案3】:

MutableLiveData 是从 LiveData 扩展而来的。 LiveData 的受保护方法只能由自身或子类处理。所以在这种情况下,作为 LiveData 子类的 MutableLiveData 可以访问这些受保护的方法。

你想做的是观察一个实例,看看是否有任何变化。但与此同时,您不希望任何“局外人”改变您正在观察的那个实例。从某种意义上说,这会产生一个问题,因为您希望有一个可更改的对象来更新任何新状态,而不是可更改的,以确保任何不应该更新此实例的人。这两个特性相互冲突,但可以通过创建一个额外的层来解决。

因此,您要做的就是使用可以访问其方法的类来扩展您的类 LiveData。子层,在本例中为 MutableLiveData,能够访问其父层 (/super) 的受保护方法。

现在您开始创建实例,并创建 MutableLiveData 的观察者实例。同时,您创建一个引用同一实例的 LiveData 实例。因为 MutableLiveData 扩展了 LiveData,所以任何 MutableLiveData 实例都是 LiveData 对象,因此可以被 LiveData 变量引用。

现在这个技巧几乎完成了。您只公开 LiveData 实例,没有人可以使用其受保护的方法,也不能将其转换为超级(可能在编译时,但它不会运行:运行时错误)。并且您将实际的子类实例保持为私有,因此它只能由拥有该实例的人使用实例的方法进行更改。

//create instance of the sub class and keep this private
private val _name: MutableLiveData<String> = MutableLiveData<String>()
//create an instance of the super class referring to the same instance
val name: LiveData<String> = _name
//assign observer to the super class, being unable to change it
name.value.observe(.....)

现在超类会在应用任何更改时发出通知。

//change the instance by using the sub class
_name.postValue(...)
//or _name.setValue(...)

块引用 一般来说,这种继承形式(增加某些方法的可见性是唯一的变化)是一种众所周知的做法吗?在哪些情况下它可能有用(假设我们可以访问所有代码)?

是的,这是众所周知的,上面描述的是一种常见的情况。删除观察者模式,并将其设置为 set/get 形式将同样受益。取决于你在哪里实现它,最终没有黄金法则。

【讨论】:

以上是关于为啥 LiveData 有一个单独的 MutableLiveData 子类?的主要内容,如果未能解决你的问题,请参考以下文章

为啥新连接的观察者会触发两次 LiveData 观察者

为啥 LiveData 没有从协程更新?

为啥观察 LiveData 时不调用 onChanged()

JetPack架构---LiveData的使用与示例

具有多个不同类型来源的 LiveData

Android知识点:LiveData为啥连续postValue两次,第一次的值会丢失?