概述
LiveData-原理全解析
LiveData是什么?
LiveData 是 Jetpack 推出的基于观察者的消息订阅/分发的可观察数据组件,具有宿主(Activity、Fragment)生命周期感知能力,这种感知能力可确保 LiveData 仅分发消息给处于活跃状态的观察者,即只有处于活跃状态的观察者才能收到消息。
而LiveData 的事件分发机制,会根据监听者的活跃状态来判断是否分发数据源变化事件,这样的话,我们就能避免当前页面在后台时,响应了事件,做出一些无用的逻辑浪费性能。
LiveData的特征
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 [Observer]对象并把最新数据派发给它。观察者可以在收到onChanged事件时更新界面,而不是在每次数据发生更改时立即更新界面。
不再需要手动处理生命周期
只需要观察相关数据,不用手动停止或恢复观察。LiveData 会自动管理Observer的反注册,因为它能感知宿主生命周期的变化,并在宿主生命周期的onDestory自动进行反注册。因此使用LiveData做消息分发不会发生内存泄漏
数据始终保持最新状态
如果宿主的生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
支持黏性事件的分发
即先发送一条数据,后注册一个观察者,默认是能够收到之前发送的那条数据的
LiveData的几种用法
声明一个LiveData
我们发现LiveData是一个抽象类,它的默认实现子类是MutableLiveData,但是看源码他们没有区别~唯一区别就是set和post方法公开了,之所以这么设计,是考虑到单一开闭原则,只有拿到 MutableLiveData 对象才可以发送消息,LiveData 对象只能接收消息,避免拿到 LiveData 对象时既能发消息也能收消息的混乱使用。
//1.声明一个MutableLiveData
val data = MutableLiveData("Test")
fun main(){
//2.监听数据源变化
data.observe(this){ data->
//...do somethig
}
}
组合多个LiveData统一观察
当我们有多个LiveData时候,某些场景下我们想统一监听,那这个时候我们可以使用MediatorLiveData来对多个LiveData进行统一监听。
//创建两个长得差不多的LiveData对象
LiveData<Integer> liveData1 = new MutableLiveData();
LiveData<Integer> liveData2 = new MutableLiveData();
//再创建一个聚合类MediatorLiveData
MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
//分别把上面创建LiveData 添加进来。
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);
Observer observer = new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer s) {
titleTextView.setText(s);
}
//一旦liveData或liveData发送了新的数据 ,observer便能观察的到,以便 统一处理更新UI
转换数据
比如我们希望对一个Int值的LiveData在监听里变成String,那么我们可以用到Transformations.map 操作符进行该操作
MutableLiveData<Integer> data = new MutableLiveData<>();
//数据转换
LiveData<String> transformData = Transformations.map(data, input -> String.valueOf(input));
//使用转换后生成的transformData去观察数据
transformData.observe( this, output -> {
});
//使用原始的livedata发送数据
data.setValue(10);
LiveData核心方法
方法名 | 作用 |
---|---|
observe(LifecycleOwner owner,Observer observer) | 注册和宿主生命周期关联的观察者 |
observeForever(Observer observer) | 注册观察者,不会反注册,需自行维护 |
setValue(T data) | 发送数据,没有活跃的观察者时不分发。只能在主线程。 |
postValue(T data) | 和setValue一样。不受线程环境限制, |
onActive | 当且仅当有一个活跃的观察者时会触发 |
inActive | 不存在活跃的观察者时会触发 |
LiveData事件分发原理
接下来我们将会了解以下几项:
- LiveData事件分发原理
- 为什么监听器在活跃时才收到事件?
- 反注册是怎么做到的?
- 粘性事件是怎么产生的?
LiveData注册流程
首先我们先看注册流程,我们可以了解到:
- 为什么监听器在活跃时才收到事件?
- 反注册是怎么做到的?
- 粘性事件是怎么产生的?
先从LiveData中的observe方法开始介绍,首先监听的所在的宿主要实现Lifecycle,然后LiveData会将监听结合宿主的生命周期做到了以上三件事~接着我们来看看源码吧!
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//1.如果注册的时候,监听所在宿主生命周期已经结束了,则不注册
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//2.将宿主和监听器进行包装,这一步是实现反注册、粘性事件、监听器活跃时收到事件的关键
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
//3.如果以前已经添加过,则报错
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
//将包装好的LifecycleBoundObserver开始监听监听器所在宿主的声明周期状态
owner.getLifecycle().addObserver(wrapper);
}
从上面源码我们可以看出,LiveData注册的关键在于LifecycleBoundObserver这个类,接着我们继续看看LifecycleBoundObserver做了什么?
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
//构造函数
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
//... 省略部分代码
}
我们聚焦构造函数,发现只保存了监听的宿主,那我们继续看看父类做了啥:
private abstract class ObserverWrapper {
//保存了监听对象
final Observer<? super T> mObserver;
//当前的监听器所在的宿主是否活跃状态
boolean mActive;
//版本号,用来对齐LiveData中的版本号用
int mLastVersion = START_VERSION;
//构造函数只负责保存了监听器
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
//判断宿主的状态是否发生改变,如果是活跃状态则根据版本号判断是否走事件分发逻辑
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
//如果是监听者的宿主正活跃,则进行事件分发(这里是活跃时才收到回调的关键实验)
if (mActive) {
dispatchingValue(this);
}
}
}
可以看出来,构造函数好像只是为了保存监听器而已,那么既然LifecycleBoundObserver同样也是LifecycleObserver,那么我们判断,逻辑的开始应该在生命周期的事件回调中,事不宜迟,我们go:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
//1.判断当前监听所在宿主的状态是否已经销毁
if (currentState == DESTROYED) {
//1.1 如果目前宿主已经销毁,则进行反注册(这里是反注册的关键)
//因为是内部类,所以调用removeObserver则会调到去外部类LiveData的函数
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
//2.这里会进行至少一次的状态对齐
while (prevState != currentState) {
prevState = currentState;
//调用该函数意义在于判断状态有没变化,如果有变化则走事件分发逻辑(这里是粘性事件的关键)
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
//...省略部分代码
}
注册的流程基本上看完了,接着我们用文字来描述一下,将一个Observer注册到LiveData会经历了什么?
- 首先会将Observer与其宿主包装成一个WrapObserver,继承自LifecycleObserver
- 宿主将WrapObserver注册到Lifecycle监听自身状态,此时会触发Lifecycle的事件回调
- 判断监听的宿主当前状态,是否已经销毁,如果是的话则进行反注册
- 如果不是,则进行至少一次的状态对齐,如果当前监听的宿主是活跃的则继而触发事件分发逻辑
- 如果版本号不一致,则进行触发监听器,同步数据源(粘性事件)
从上面的流程,大家也应该明白了以下原理:
- 反注册的逻辑
- 活跃时候分发事件的逻辑
- 粘性事件是怎么诞生的
LiveData事件分发流程
那么,注册的过程算是说完了,接着要说事件是怎么分发的了,就是通过setValue/postValue更新数据源后,会发生什么,我们先看看postValue
postValue
postValue自带切换主线程的能力,我们看看postValue是怎么进行线程切换的:
public abstract class LiveData<T> {
static final Object NOT_SET = new Object();
//用于线程切换时,暂存Data用的变量
volatile Object mPendingData = NOT_SET;
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
//1.判断是否为一次有效的postValue
postTask = mPendingData == NOT_SET;
//2.暂存对象到mPendingData中
mPendingData = value;
}
//3.过滤无效postValue
if (!postTask) {
return;
}
//4.这里通过ArchTaskExecutor切换线程,其实ArchTaskExecutor切换线程的核心靠一个掌握着MainLooper的Handler切换,这里不展开说了
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
//用于给Handler执行切换线程的Runnable
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
//5.从缓存中获得Data
newValue = mPendingData;
mPendingData = NOT_SET;
}
//6.通过setValue设置数据
setValue((T) newValue);
}
};
}
通过postValue的源码我们可以得知,postValue主要的责任是利用ArchTaskExecutor将线程切换到主线程,最后通过setValue设置数据, 那么我们就去看看setValue的具体实现吧~最后一步啦!
setValue(T data)
事件分发的关键函数,我们通过setValue能看到整个分发的机制是怎么样的:
@MainThread
protected void setValue(T value) {
//1.判断当前是否主线程,如果不是则报错(因为多线程会导致数据问题)
assertMainThread("setValue");
//2.版本号自增
mVersion++;
//3.数据源更新
mData = value;
//4.分发事件逻辑
dispatchingValue(null);
}
//事件分发的函数
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//5.判断是否分发中,如果是的话,忽略这次分发
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
//6.设置表示
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
//7.判断参数中,有没指定Observer,如果有则只通知指定Observer,没有的话则遍历全部Observer通知
if (initiator != null) {
//7.0
considerNotify(initiator);
initiator = null;
} else {
//7.1 遍历全部Observer通知更新
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
//分发函数
private void considerNotify(ObserverWrapper observer) {
//8. 判断如果当前监听器不活跃,则不分发
if (!observer.mActive) {
return;
}
//9. 二次确认监听器所在宿主是否活跃,如果不活跃,则证明Observer中的mActive状态并非最新的,调用activeStateChanged更新状态
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//10. 判断版本号是否一致,如果一致则不需要分发
if (observer.mLastVersion >= mVersion) {
return;
}
//11. 对齐版本号
observer.mLastVersion = mVersion;
//12. 通知监听器
observer.mObserver.onChanged((T) mData);
}
以上就是LiveData中事件分发的逻辑原理,通过注释加序号解释了整个分发流程,如果看不明白的话可以重新多看几次,理解LiveData的事件分发逻辑是怎么样的。
最后
以上就是虚拟诺言为你收集整理的LiveData-原理全解析LiveData-原理全解析的全部内容,希望文章能够帮你解决LiveData-原理全解析LiveData-原理全解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复