Home Livedata使用以及原理解析
Post
Cancel

Livedata使用以及原理解析

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者,也会在应用组件销毁时自动取消关联。

基础使用

LiveData作为一个支持范型的数据对象,一般是放置在ViewModel中的。并且LiveData是一个抽象类,不能直接使用,只能使用其子类

1
2
3
class JetPackModel : ViewModel() {
    val count = MutableLiveData(0)
}

于此同时LiveData是一个被观察者,可以向其添加观察者对象,当内部的数据发生变化时会主动去通知观察者。所以被观察者一般都会具有以下类型的方法:

  • 添加观察者的方法,observeForever(Observer<? super T> observer)向LiveData添加一个观察者

  • 改变其内部的数据的方法,对应LiveData有setValue (必须主线程调用)和postValue(可以在子线程调用),数据更改之后会去通知观察者,而这两个方法是protected的,不可以直接使用,需要使用其子类MutableLiveData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class JetPackModel : ViewModel() {
    private val _count = MutableLiveData(0)
    // 对外暴露不可变的LiveData
    val count: LiveData<Int> = _count

    fun click() {
        val value = _count.value ?: 0
        // setValue方法只能在主线程调用,会立即触发活跃状态观察者的Obsrever方法
        _count.value = value + 1
    }
    fun postClick(){
        val value = _count.value ?: 0
        // 会post到主线程中进行
        _count.postValue(value + 1)
    }
}
override fun onCreate(savedInstanceState: Bundle?) {
    // 当数据改变时会回调onChanged方法
    model.count.observeForever(object :Observer<Int>{
        override fun onChanged(t: Int?) {  
        }
    })
}

但是这样的被观察者在使用中需要在onDestory中主动去移除,否则会存在一些问题

  • 当Activity销毁时,LiveData依旧会持有Observer对象,造成Activity的泄漏
  • 此时LiveData的更新,很可能因为Activity的销毁而导致崩溃

但作为LiveData而言,是一个具有生命的Data,要解决上面的问题可以使用他的observe(LifecycleOwner owner, Observer<? super T> observer),结合Lifecycle实现自动解绑可以有效的防止内存泄漏。与此同时,结合Lifecycle之后的LiveData还具有如下优势

  • 只会去通知处于活跃状态的观察者。
  • 当由非活跃状态改变为活跃状态时,若有新的数据会立即更新。
1
2
3
4
5
6
7
override fun onCreate(savedInstanceState: Bundle?) {
    // 当数据改变时会回调onChanged方法
    model.count.observe(this, object :Observer<Int>{
        override fun onChanged(t: Int?) {
        }
    })
}

当LiveData所装载的数据和想对外暴露的数据有所区别时,可以对其进行一定的转化。

1
2
3
4
private val _count = MutableLiveData<Int>()
val clickText: LiveData<String> = Transformations.map(_count){
    "this button click $it times"
}

当一个界面的数据依赖于多个数据源时,可以使用

LiveData还可以结合网络请求、Room、DataStore来使用,有助于界面上显示的数据与数据库中的保持一致。

实现原理

Q:LiveData作为一个可以结合生命周期使用的被观察者,他是怎么协同Lifecycle处理如下问题的呢?

  • 生命周期结束时自动解绑
  • 如何区分活跃状态于非活跃状态的观察者,指定更新活跃状态的观察者
  • 观察者变为活跃状态时更新最新的数据

因为一个LiveData可以在绑定多个具有生命周期的观察者对象,且生命周期Lifecycle很可能是不同的,所以大致对应关系可能如下

为了区别观察者的状态去选择性的更新以及对Lifecycle状态改变时做出处理,LiveData就需要对observer进行包装,大概会涉及到如下几个类,都是LiveData的内部类

并且添加进来的观察者有两种类型(带不带生命周期),所以LiveData对其进行了区分,使用了ObserverWrapper来约定了观察者的基础特性,以及两个子类扩展其自己的特性。

  • ObserverWrapper:含有几个关键的标志属性和方法
    • mObserver - 真正的观察者对象
    • mActive - 是否活跃的标志
    • mLastVersion - 当前观察者数据的version
  • AlwaysActiveObserver 无生命周期的观察者,改变了就得收到通知,所以属于一直是活跃的
  • LifecycleBoundObserver 带生命周期的观察者,实现了LifecycleEventObserver接口,所以同时也是Lifecycle的观察者

首先从对应的方法入手,observe方法就把生命周期对象LifecycleOwner传入了进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// LiveData.java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // LiveData的相关操作都是必须在主线程完成的
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 用LifecycleBoundObserver对observer进行代理
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 一个LiveData中不能重复添加同一个observer
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 然后与lifecycle绑定,生命周期状态改变时就会通知wrapper了
    owner.getLifecycle().addObserver(wrapper);
}

LiveData真正通知观察者主要有两种情况:

  • 进行了setValue或postValue: 遍历所有观察者,若活跃才通知
  • lifecycle状态改变,观察者状态由非活跃切换为活跃状态: 判断是否是变成的活跃状态,若是才去通知

所以我们先看lifecycle状态改变时的调用流程

所以当lifecycle的状态改变的时候,会把消息传递给部分observer对象,下面是LifecycleBoundObserver的onStateChanged方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// LiveData$LifecycleBoundObserver.class
public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event) {
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    // 如果当前状态是destroyed,则移除观察。
    if (currentState == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    // 同步状态
    Lifecycle.State prevState = null;
    while (prevState != currentState) {
        prevState = currentState;
        activeStateChanged(shouldBeActive());
        currentState = mOwner.getLifecycle().getCurrentState();
    }
    // 大于等于started状态即start和resume
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
}

首先会获取当前的状态,若已经是destroy状态则移除观察直接返回然后开始更新状态。LiveData的具有惰性:如果是若变为活跃状态,有需要更新的数据才通知观察者。对于带状态的观察者来说,并不需要很细的生命周期状态,只需要是否活跃的区分。再看activeStateChanged方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// LiveData$ObserverWrapper.class
void activeStateChanged(boolean newActive) {
	// 如果状态已经一样了,直接返回
    if (newActive == mActive) {
        return;
    }
    mActive = newActive;
    // 改变活跃的观察者数量,如果这个变为活跃则加一否则减一
    changeActiveCounter(mActive ? 1 : -1);
    if (mActive) {
        // 如果当前是变更为活跃状态,则开始通知观察者
        dispatchingValue(this);
    }
}

因为没有状态的改变,所以直接返回即可。changeActiveCounter方法主要统计活跃的观察者数量。当从非活跃状态变为活跃状态时,则去判断是否需要同步数据给观察者。

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
// LiveData.java
void changeActiveCounter(int change) {
    int previousActiveCount = mActiveCount;
    mActiveCount += change;
    // 如果是正在改变,则直接返回
    if (mChangingActiveState) {
        return;
    }
    mChangingActiveState = true;
    try {
        while (previousActiveCount != mActiveCount) {
            // 如果活跃的观察者数量由没有变成了有
            boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
            // 如果活跃的观察者一个都没有了
            boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
            previousActiveCount = mActiveCount;
            // 调用LiveData的状态切换方法
            if (needToCallActive) {
                onActive();
            } else if (needToCallInactive) {
                onInactive();
            }
        }
    } finally {
        mChangingActiveState = false;
    }
}

这里主要是对活跃的观察者数量由0到1或由1到0时触发LiveData的两个状态切换方法:onActive()和onInactive(),这个类似与ProcessLifecycleOwner中观察activity状态切换来控制应用进程状态切换。

然后接下来是dispatchingValue方法

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
// LiveData.java
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
        	// 如果initiator不为空则去看是否调用观察者
            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;
}

dispatchingValue是LiveData中的一个综合分发方法,若initiator不为空则尝试指定推送,若为空的话会尝试通知全部。并且mDispatchingValue表示是否正在进行这个方法,而mDispatchInvalidated用于中断遍历,重新全部更新。

接下来是considerNotify方法,主要判断是否真正调用observer的onChanged方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    // 是否有最新的数据
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 真正通知
    observer.mObserver.onChanged((T) mData);
}

这里进行几次状态的检验,保证是Observer是活跃并且LiveData有新的消息才去调用onChanged。

setValue触发更新的流程

1
2
3
4
5
6
7
8
9
// LiveData.java
protected void setValue(T value) {
    assertMainThread("setValue");
    // 设置最新的数据版本
    mVersion++;
    mData = value;
    // 全局遍历通知
    dispatchingValue(null);
}

然后post的设置方式

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
// LiveData.java
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 通过Handler切换回主线程执行
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

通过mPendingData来标志现在是否有没执行的更新操作,若有的话就直接更新值不用再发handler事件了。

最后看一下observeForever方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// LiveData.java
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    // 生成代理对象
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 设置状态为active
    wrapper.activeStateChanged(true);
}
// LiveData$AlwaysActiveObserver.class
boolean shouldBeActive() {
    // 表示一致都需要同步,LiveData改变就要通知
    return true;
}

总结

  • LiveData会对添加进来的方法进行包装,增加mActive等属性
  • 若传入了LifecycleOwner,则也会作为lifecycle的观察者,观察Lifecycle的生命周期变化,在合适的时候进行解绑以及通知更新数据等操作
  • 当setValue时会对所有包装的观察者对象进行遍历,通知活跃的观察者
This post is licensed under CC BY 4.0 by the author.

Lifecycle使用以及原理解析

Room使用以及原理解析