LiveData

简述:

数据实时更新、安全更新:当liveData的setValue被调用时,会遍历自身所有observer并considerNotify,此时会判断observer的活跃状态(shouldBeActive)和内部版本,决定是否向其发送通知。

避免内存泄漏:传入lifeCycleOwner即走observe()的LiveData会在lifeCycleOwner回调onStateChanged为DESTROYED的时候移除掉observer防止内存泄露,而相对的observeForever则由于没有owner而无此特性。

解决Configuration Change问题、粘性数据:一般而言observer是在onCreate中调用的,liveData.observer方法在调用时也会立即被推送liveData中最后一次数据。(会导致所谓的数据倒灌)。Configuration Change时会ViewModel会从当前Activity或fragmentManager的viewModelStore的缓存中重新取出,而ViewModel中的liveData自然也会保留。

https://zhuanlan.zhihu.com/p/593472898

Featrue

  • UI和实时数据保持一致 因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。

  • 避免内存泄漏 观察者被绑定到组件的生命周期上,当被绑定的组件销毁(destroy)时,观察者会立刻自动清理自身的数据。

  • 不会再产生由于Activity处于stop状态而引起的崩溃,例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。

  • 不需要再解决生命周期带来的问题 LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。

  • 实时数据刷新 当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据。

  • 解决Configuration Change问题 在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。

常用实现类

https://chatgpt.com/c/21580ee8-b685-48be-a510-712388ae346e

LiveData常用于MVVM中,ViewModel向View层提供订阅的单向数据源。一般实践中会根据”单一数据源真相”原则,在ViewModel层中收敛所有的可变MutableLiveData,只对View层提供不可变的LiveData。View层行为应通过调用ViewModel的方法变更数据。

在MVVM架构中,View层不应该直接改变ViewModel中的数据状态。这是为了保持清晰的职责分离和确保数据的一致性。具体来说:

  • View层:主要负责显示数据和处理用户交互。它可以观察LiveData并根据数据变化更新UI。
  • ViewModel层:负责准备和管理与UI相关的数据。它通过MutableLiveData更新数据,并暴露LiveData给View层以供观察。
  • Model层:负责数据操作,如网络请求和数据库操作。

工作流程

  1. View观察ViewModel中的LiveData:View层通过观察ViewModel中暴露的LiveData,接收数据变化并更新UI。
  2. 用户交互通过View通知ViewModel:当用户与UI交互时,View层调用ViewModel中的方法,传递用户动作或输入。
  3. ViewModel更新数据:ViewModel处理用户的输入,进行必要的业务逻辑处理,并通过更新MutableLiveData来改变数据状态。
  4. View层收到更新并更新UI:由于View观察了LiveData,当ViewModel中的数据状态发生变化时,View会自动收到通知并更新UI。

假设你有一个按钮点击事件来更新数据:

1
2
3
4
5
6
7
8
// 在View层(例如Activity或Fragment)中
viewModel.buttonClicked.observe(this, Observer { newText ->
textView.text = newText
})

button.setOnClickListener {
viewModel.onButtonClicked()
}

在ViewModel中:

1
2
3
4
5
6
7
8
9
class TestViewModel : ViewModel() {
private val _buttonClicked: MutableLiveData<String> = MutableLiveData()
val buttonClicked: LiveData<String> = _buttonClicked

fun onButtonClicked() {
// 更新数据
_buttonClicked.value = "按钮点击了"
}
}

关键点

  1. View层不直接修改数据:View层通过调用ViewModel中的方法来请求数据更新,而不是直接修改LiveData
  2. ViewModel负责数据更新:ViewModel接收View层的请求,更新MutableLiveData中的数据。
  3. 数据变化通知View层LiveData的数据变化会通知观察者(View层),并更新UI。

通过这种方式,确保了View层与ViewModel层之间的职责清晰分离,并且数据状态的管理是集中且可控的。

LiveData

不可变的(一般作为ViewModel对View层提供的单向数据源)

MutableLiveData

可变的(一般用于ViewModel层内部变更数据,会用对外提供LiveData同名加下划线前缀命名)

https://developer.android.com/reference/android/arch/lifecycle/MutableLiveData

MediatorLiveData

可变的、监听多源的

https://developer.android.com/reference/android/arch/lifecycle/MediatorLiveData

原理

我们知道 livedata 的使用很简单,它是采用观察者模式实现的

  1. 添加观察者
  2. 在数据改变的时候设置 value,这样会回调 Observer 的 onChanged 方法
1
2
3
4
5
6
7
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}

observe方法

LiveData包含两个用于添加数据观察者(Observer)的方法,分别是

  • observe(LifecycleOwner , Observer) 生命周期安全的
  • observeForever(Observer)

两个方法的区别对于外部来说只在于是否提供了生命周期安全的保障。

生命周期安全的observe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//限定只能在主线程调用 observe 方法
assertMainThread("observe");
//当 Lifecycle 已经处于 DESTROYED 状态时,此时进行 observe 是没有意义的,直接返回
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//根据传入参数构建一个新的代理 Observer
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//将 observer 作为 key,wrapper 作为 value 进行存储
//当 mObservers 不包含该 key 时,调用 putIfAbsent 会返回 null
//当 mObservers 已包含该 key 时,调用 putIfAbsent 不会存储 key-value,并会返回之前保存的 value
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
//走到此步,说明之前 LiveData 内部已经持有了 observer 对象,
//且该 observer 对象已经绑定了其它的 LifecycleOwner 对象
//此时直接抛出异常
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
//observer 之前已经传进来过了,此处直接返回
return;
}
owner.getLifecycle().addObserver(wrapper);
}

传入的LifecycleOwner参数意味着携带了Lifecycle对象,LiveData内部就根据 Lifecycle的生命周期事件的回调变化在合适的时机进行数据通知,并在 Lifecycle对象处于DESTROYED状态时自动移除Observer,这也是LiveData避免内存泄漏的最重要的一个点。

上面的代码使用到了LifecycleBoundObserver,它是抽象类ObserverWrapper的实现类。ObserverWrapper用于包装外部传进来的Observer对象,为子类定义好特定的抽象方法和共用逻辑,主要是提供了共用的状态分发函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;

LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}

@Override
boolean shouldBeActive() {
//只有当 Lifecycle 的当前状态是 STARTED 或者 RESUMED 时
//才认为 Lifecycle 是处于活跃状态
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

//LifecycleEventObserver 的实现方法
//当 Lifecycle 的生命周期状态发生变化时就会调用此方法
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//如果 Lifecycle 已经处于 DESTROYED 状态了,则主动移除 mObserver
//这就是 LiveData 可以避免内存泄露最重要的一个点
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}

@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}

@Override
void detachObserver() {
//移除 mObserver
mOwner.getLifecycle().removeObserver(this);
}
}

LifecycleBoundObserver会保证当LiveData依赖的生命周期转到非活跃时,不进行通知(如果DESTROY会直接移除Observer);当转到活跃时且数据版本更新了则向其发起回调。

LifecycleBoundObserver的整个事件流程是这样的:

  1. Lifecycle的生命周期发生变化,从而回调了onStateChanged函数
  2. onStateChanged函数首先判断Lifecycle是否已处于DESTROYED状态,是的话则直接移除Observer,整个回调流程结束,否则则继续以下流程
  3. onStateChanged调用了activeStateChanged()函数,activeStateChanged()函数判断Lifecycle的活跃状态是否发生了变化,如果从非活跃状态切换到了活跃状态,是的话则调用dispatchingValue()函数来分发值,最终再根据ObserverWrapper内部的value版本号mLastVersion来判断是否有新值需要向其回调,是的话则向其回调新值,否则则返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private abstract class ObserverWrapper {

//外部传进来的对 LiveData 进行数据监听的 Observer
final Observer<? super T> mObserver;

//用于标记 mObserver 是否处于活跃状态
boolean mActive;

//用于标记 Observer 内最后一个被回调的 value 的新旧程度
int mLastVersion = START_VERSION;

ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}

//用于获取当前 Lifecycle 是否处于活跃状态
abstract boolean shouldBeActive();

//用于判断 mObserver 是否和 LifecycleOwner(即 Lifecycle)有绑定关系
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}

//移除 mObserver
void detachObserver() {
}

void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
//判断当前 LiveData 所有的 Observer 是否都处于非活跃状态
boolean wasInactive = LiveData.this.mActiveCount == 0;
//更新 LiveData 当前所有处于活跃状态的 Observer 的数量
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
//如果 LiveData 处于活跃状态的 Observer 数量从 0 变成了 1,
//则回调 onActive 方法
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
//如果 LiveData 处于活跃状态的 Observer 数量从 1 变成了 0,
//则回调 onInactive 方法
onInactive();
}
if (mActive) {
//如果 mObserver 变成了活跃状态,则向其回调新值
dispatchingValue(this);
}
}
}

ObserverWrapper一共有两个子类:

LifecycleBoundObserver和AlwaysActiveObserver,两者的差别就在于是否和生命周期相绑定

非生命周期安全的observeForever

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
//限定只能在主线程调用 observe 方法
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
//会走到这一步,是因为之前已经先用该 observer 对象调用了 observe(LifecycleOwner,Observer)
//这里直接抛出异常
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
//如果之前已经添加过 observer 对象了的话,则直接返回
return;
}
//主动触发 activeStateChanged 函数,因为当前 LiveData 可能已经被设置值了
wrapper.activeStateChanged(true);
}

上面代码使用到了AlwaysActiveObserver,它也是抽象类ObserverWrapper的实现类,其shouldBeActive()返回值固定为true,意味着只要有数据变化都会进行回调。所以使用observeForever()函数一定要在过后主动移除Observer,避免内存泄露和NPE。

更新LiveData的值

更新LiveData的值的方法一共有两个,分别是:

  • setValue(T value)
  • postValue(T value)

setValue

setValue(T)函数被限定在只能主线程进行调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");

//更新当前 value 的版本号,即 value 的新旧程度
mVersion++;

mData = value;
dispatchingValue(null);
}

dispatchingValue()函数设计得比较巧妙,用两个全局的布尔变量mDispatchingValue和mDispatchInvalidated就实现了新旧值判断、旧值舍弃、新值重新全局发布的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//initiator 为 null 则说明需要遍历回调整个 mObservers
//initiator 不为 null 则说明仅回调 initiator 本身
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//如果当前正处于向 mObservers 发布 mData 的过程中(即 mDispatchingValue 为 true)
//则将 mDispatchInvalidated 置为 true,用于标明有新值到来,正在回调的值是已经过时的了
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 判断是否要将数据分发到指定的 ObserverWrapper
private void considerNotify(ObserverWrapper observer) {
//如果 observer 处于非活跃状态,则直接返回
if (!observer.mActive) {
return;
}
//此处判断主要是为了照顾 LifecycleBoundObserver
//由于 Lifecycle 有可能状态值 State 已经切换到了非活跃状态,但 LifecycleBoundObserver 还未收到事件通知
//所以为了避免意外情况,此处主动检查 observer 的活跃状态并判断是否需要更新其活跃状态
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//根据 observer 本部的 value 版本号 mLastVersion 来决定是否需要向其进行回调
//为了避免重复向某个 observer 回调值,所以此处需要判断下
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}

postValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

postValue(T)函数不限定调用者所在线程,不管是主线程还是子线程都可以调用,因此是存在多线程竞争的可能性的,postValue(T)函数的重点旧在于需要理解其从子线程切换到主线程之间的状态变化。

在mPostValueRunnable被执行前,所有通过postValue(T)函数传递的value都会被保存到变量mPendingData上,且只会保留最后一个,直到mPostValueRunnable被执行后mPendingData才会被重置,所以使用 postValue(T) 函数在多线程同时调用或者单线程连续调用的情况下是存在丢值(外部的 Observer 只能接收到最新值)的可能性的。

Issue

粘性事件

LiveData 本身被设计为粘性事件,也即,一旦 LiveData 持有数据,那么在观察者订阅该 LiveData 时,会被推送最后一次数据。(当observe时会立即回调onChange推送数据)

数据倒灌(其实就是粘性事件,在多级页面中)

当页面重建时多级fragment订阅其activity的ShareViewModel的liveData 被 LiveData 回推脏数据

https://xiaozhuanlan.com/topic/6719328450

https://medium.com/@kunminx/livedata-%E6%95%B0%E6%8D%AE%E5%80%92%E7%81%8C-%E5%88%AB%E9%97%AE-%E9%97%AE%E5%B0%B1%E6%98%AF%E4%B8%8D%E5%8F%AF%E9%A2%84%E6%9C%9F-5fc02fec76e0

e.g:

设想一下这样的场景,一级页面是列表,二级页面是只读的详情预览,三级页面是可编辑的详情,

当我们在编辑页修改完信息时,我们通常会通过回调来通知一二级页面刷新信息。此时如是通过 LiveData 来通知,便存在隐患,

image-20231010115452599

因为跨页面通信使用的是 Activity 级作用域的 SharedViewModel,其生命周期长于 Fragment,乃至当 “编辑页”、“预览页” 出栈时,SharedViewModel 和其旗下的 LiveData 还存在于内存中,

那么下一次从 “列表页” 跳到 “预览页”,“预览页” 一注册 LiveData,就会因为粘性设定,而收到上一次从 “编辑页” 发来的旧信息,这明显不符合预期,

现有解决方案及各自缺陷

或应参考这篇文章https://juejin.cn/post/7268622342728171572,其中评论中提出

个人见解,
方式是好方式,看了许多解决数据倒灌的文章,但是数据倒灌感觉并不是一个问题。
livedata本身是用于描述状态的,而不是事件的。
描述事件一般用publishsubject或shareflow或eventbus,再不济自己写个listener也行。 //ps:上flow
因此感觉用livedata描述事件本身违背了livedata的设计初衷。

《Jetpack MVVM 精讲》中我分别提到了 Event 事件包装器、反射方式、SingleLiveEvent 这三种方式来解决 “数据倒灌” 的问题。它们分别来自上文我们提到的外网美团的文章,和官方最新 demo

分别存在如下问题:

Event 事件包装器

对于多观察者的情况,只允许第一个观察者消费,这不符合现实需求;

而且手写 Event 事件包装器,在 Java 中存在 null 安全的一致性问题。

反射干预 Version 的方式:美团LiveDataBus

存在延迟,无法用于对实时性有要求的场景;

并且数据会随着 SharedViewModel 长久滞留在内存中得不到释放。

https://github.com/JeremyLiao/LiveDataBus

官方最新 demo 中的 SingleLiveEvent (已移除)

是对 Event 事件包装器 一致性问题的改进,但未解决多观察者消费的问题;

而且额外引入了消息未能从内存中释放的问题。

UnPeekLiveData 特点

UnPeekLiveData 通过 独创的 “延时自动清理消息” 的设计,来满足:

1.消息被分发给多个观察者时,*不会因第一个观察者消费了而直接被置空*

2.时限到了,*消息便不再会被倒灌*

3.时限到了,*消息自动从内存中清理释放*

4.使非入侵的设计成为可能,并最终结合官方 SingleLiveEvent 的设计实现了 ***遵循开闭原则的非入侵重写***

连续postValue()吞数据

Google在postValue的注释上说明了

If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
如果在主线程执行一个已发布的任务之前多次调用此方法,则只会分派最后一个值。

img

只有第一次调用时会走到postToMainThread(mPostValueRunnable),后面所有执行的postValue在执行完postTask = mPendingData == NOT_SET 和 mPendingData = value 后,直接由于之后postTask == false,所以return掉了,也就不会再postToMainThread一次而是只修改mPendingData数据。

直到主线程中mPostValueRunable执行方法中会将真正调用setValue并将mPendingData重置为NOT_SET.

这个实现其实听起来很像View的刷新,比如TextView连续设置两次文本,也是只有当Vsync到来时取最新的文本设置到TextView中

Author

white crow

Posted on

2022-02-24

Updated on

2024-06-06

Licensed under