源码学习 - LiveData

82 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

本文为源码学习文章之一,记录源码的阅读思考

Talk is cheap. Show me the code.

缘分渊源 - Intro

移动应用架构谈 - MVI一文中,提到了界面层可以使用LiveData来订阅数据层的数据变化。因此本文主要是在阅读学习相关源码的过程中来分析LiveData是如何运作的,以期能更好的使用LiveData,并能对实现中的细节点进行学习和借鉴。

准备工作 - Depends

  • 观察者模式: 参考《设计模式》中的定义是

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

  • 命令式编程 VS 响应式编程
  • LiveData版本
dependencies {
    def lifecycle_version = "2.5.1"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

源码源码源码 - Code

LiveData is a data holder class that can be observed within a given lifecycle

根据代码注释,LiveData是一种可观察的数据存储器类,属于架构组件/界面层库/生命周期感知型组件,是Android Jetpack的一部分,LiveData拆解下来所提供的能力是:

  • 数据存储 - data holder
  • 可观察 - can be observed
  • 生命周期感知 - within a given lifecycle

关于数据存储

LiveData数据存储的数据只有通过volatile进行可见性保护的最新一份mData,注意这里的没有使用范型T来定义类型,而是使用了Object,与NOT_SET状态的定义使用有关;其中要注意一个点是定义的属性中多了关于数据版本的version控制字段。

static final Object NOT_SET = new Object();

private volatile Object mData;

private int mVersion;

关于可观察

image.png

LiveData的可观察性实现,与其他观察者模式的实现一致,组件通过成对的注册与解注册观察提供可观察能力。不过Livedata是提供了两组observe/removeObserver,用于对LiveData中存储的数据进行观察。一组在注册过程中带上了LifecycleOwner,具有生命周期感知的观测能力的;另一组则是永久性的观测,注意使用过程中需要主动去释放观察。最终所有LiveData的观察者都会被存在observers链表中,链表对于动态的增加或移除的操作有优势。

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();

当观察者在激活状态,且LiveData当前持有的数据是最新数据的时候,观察者能接到数据变化的通知。需要注意

  • 观察者接收数据变化的有效区间是 [活跃, 非活跃] 区间同 [注册观察, 移除注册观察] 区间的并集区间决定的。
  • 数据是否变化是根据上一次通知观测者时的数据版本与当前数据的版本差别来决定的,这里的好处就是,通过重刷数据也能触发观察者的行为变化。 image.png

即可以用下图表示观察者收到数据变化的流程 image.png 其中

  • setValue用于主线程设置数据;postValue用于非主线程设置数据,最终调用到主线程的执行的setValue,由于post是将如下图runnable抛到主线程执行进行设置,因此存在数据设置展示丢失的风险,考虑到观察者一般只关心最后一次变化的数据,此处使用问题不大,若需要关心每一次的数据变更就要小心了。 image.png
  • 在dispathcingValue中,当LiveData持有的数据变化的时候,会检查所有注册的观察者是否满足通知条件进行通知;当某个观察者活跃状态变化的时候会被针对性的通知检测,避免了其他观察者受其他观察活跃状态变化带来通知检测,是一个通知场景优化;同时,如果是先设置了数据,观察者后注册或者注册的时候非活跃状态,那么当观察者变成活跃状态后,观察者收到了能收到上一次设置的数据,噢,LiveData是粘性的!!!

关于生命周期感知

虽然很重要,但实际上又是一个观察者模式的应用。LiveData通过注册观察函数中提供的LifecycleOwner参数,将其封装到LifecycleBoundObserver中提供了当前观察者订阅生命周期状态变化的能力,并在订阅的生命周期状态到DESTROY后,自动释放了当前的观察者,完全避免了遗忘解除观察带来的风险。

思考点

  • LiveData是粘性的

  • LiveData的观察者必须在主线程

MutableLiveData(开放变更数据能力) MediatorLiveData 框架性质的,解放了观察跟释放的一堆代码实现,合并变更数据源 如果不是这样子的方式 -> 1个observer(解耦,命令式实现) -> 多个,重复逻辑实现(隐藏实现)

使用说明 - Tips

  • 官方使用注意点

在大多数情况下,应用组件的 onCreate() 方法是开始观察 LiveData 对象的正确着手点,原因如下:

确保系统不会从 Activity 或 Fragment 的 onResume() 方法进行冗余调用。 确保 Activity 或 Fragment 变为活跃状态后具有可以立即显示的数据。一旦应用组件处于 STARTED 状态,就会从它正在观察的 LiveData 对象接收最新值。只有在设置了要观察的 LiveData 对象时,才会发生这种情况。

  • MutableLiveData

LiveData的子类,LiveData对于持有的数据的修改是封闭的,事实上是通过MutableLiveData来对持有的数据进行修改变更操作。

  • MediatorLiveData MutableLiveData的子类,提供了间接注册观察数据的能力。更倾向于说是封装了一个间接观察者框架,有利于清晰的描述观察者所需要观察影响到自身业务逻辑的状态数据。对于去耦合,提高可测试性的代码编写有积极作用

小白吐槽 - Sum

LiveData的使用跟实现没有存在特别复杂的地方。但是,如果监听状态关系复杂了,状态变更依赖关系是否会形成循环,是否可以通过某些手段构建依赖检测来确保不存在循环,关系是个DAG图。LiveData会被Flow替代么,我觉得还是要根据实际使用业务场景来决定,在好的设计定义的数据模型支持下,替换成本是可以被接受的,业务发展使用场景决定,depends。

参考

  1. LiveData 概览
  2. 观察者模式
  3. 理解【观察者模式】和【发布订阅】的区别
  4. 为什么Google要将LiveData设计成粘性的
  5. 为什么LiveData的观察者必须处于主线程中