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();//再创建一个聚合类MediatorLiveDataMediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();//分别把上面创建LiveData 添加进来。
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);Observer observer = new Observer<Integer>() {@Overridepublic 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事件分发原理

接下来我们将会了解以下几项:

  1. LiveData事件分发原理
  2. 为什么监听器在活跃时才收到事件?
  3. 反注册是怎么做到的?
  4. 粘性事件是怎么产生的?

LiveData注册流程

首先我们先看注册流程,我们可以了解到:

  1. 为什么监听器在活跃时才收到事件?
  2. 反注册是怎么做到的?
  3. 粘性事件是怎么产生的?

先从LiveData中的observe方法开始介绍,首先监听的所在的宿主要实现Lifecycle,然后LiveData会将监听结合宿主的生命周期做到了以上三件事~接着我们来看看源码吧!

 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {//1.如果注册的时候,监听所在宿主生命周期已经结束了,则不注册if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}//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 {@NonNullfinal 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// ownermActive = newActive;changeActiveCounter(mActive ? 1 : -1);//如果是监听者的宿主正活跃,则进行事件分发(这里是活跃时才收到回调的关键实验)if (mActive) {dispatchingValue(this);}}}

可以看出来,构造函数好像只是为了保存监听器而已,那么既然LifecycleBoundObserver同样也是LifecycleObserver,那么我们判断,逻辑的开始应该在生命周期的事件回调中,事不宜迟,我们go:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;@Overridepublic 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会经历了什么?

  1. 首先会将Observer与其宿主包装成一个WrapObserver,继承自LifecycleObserver
  2. 宿主将WrapObserver注册到Lifecycle监听自身状态,此时会触发Lifecycle的事件回调
  3. 判断监听的宿主当前状态,是否已经销毁,如果是的话则进行反注册
  4. 如果不是,则进行至少一次的状态对齐,如果当前监听的宿主是活跃的则继而触发事件分发逻辑
  5. 如果版本号不一致,则进行触发监听器,同步数据源(粘性事件)

从上面的流程,大家也应该明白了以下原理:

  1. 反注册的逻辑
  2. 活跃时候分发事件的逻辑
  3. 粘性事件是怎么诞生的

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.判断是否为一次有效的postValuepostTask = mPendingData == NOT_SET;//2.暂存对象到mPendingData中mPendingData = value;}//3.过滤无效postValueif (!postTask) {return;}//4.这里通过ArchTaskExecutor切换线程,其实ArchTaskExecutor切换线程的核心靠一个掌握着MainLooper的Handler切换,这里不展开说了ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}//用于给Handler执行切换线程的Runnableprivate final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {//5.从缓存中获得DatanewValue = mPendingData;mPendingData = NOT_SET;}//6.通过setValue设置数据setValue((T) newValue);}};
}

通过postValue的源码我们可以得知,postValue主要的责任是利用ArchTaskExecutor将线程切换到主线程,最后通过setValue设置数据, 那么我们就去看看setValue的具体实现吧~最后一步啦!

setValue(T data)

事件分发的关键函数,我们通过setValue能看到整个分发的机制是怎么样的:

@MainThreadprotected 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-原理全解析相关推荐

  1. bobsmith电路阻抗原理_串联谐振原理全解析 - 赫兹电力

    串联谐振赫兹电力为您导读:串联谐振原理全解析,串联谐振交流耐压试验在发电机绝缘试验中占据至关重要的地位,今天我们就来系统学习一下如何谐振及其原理解析吧. 谐振基础知识 谐振电路是在具有电阻R.电感L. ...

  2. Leon : YoloV5 结构原理全解析 思维导图版

    Leon : YoloV5 结构原理全解析 思维导图版 博客写到了 Head 部分 暂时断更 几天 --因为 互联网加的项目 快提交了 队员们 陆续 完成了 自己负责的策划书部分 得 开始 去修改策划 ...

  3. STM32 IAP 在线升级原理全解析

    点击左上角的"关注",定期更新 STM32 最新资讯,总有你想要的信息! STM32 IAP 在线升级原理全解析 1. 什么是 IAP?    IAP(In-Application ...

  4. PFC工作原理全解析

    前言 概述 原理分析 BOOST拓扑分析 BOOST电路双闭环控制 PFC工作原理与控制逻辑 大家好,我是大昌,今天给大家分析PFC的工作原理,妥妥的干货,马上开始. 概述 传统应用中输入侧交流电源经 ...

  5. 淘宝小部件 Canvas 渲染流程与原理全解析

    作者:史健平(楚奕) 上篇回顾:<淘宝小部件:全新的开放卡片技术!>.<淘宝小部件在 2021 双十一中的规模化应用> 本文主要从技术视角阐述 Canvas 在小部件下的渲染原 ...

  6. Webpack HMR 原理全解析

    执行 npx webpack serve 命令后,WDS 调用 HotModuleReplacementPlugin 插件向应用的主 Chunk 注入一系列 HMR Runtime,包括: 用于建立 ...

  7. HashMap底层原理全解析

    作为面试中的高频题目,我相信每一个java程序员都有必要搞懂HashMap的底层原理和实现细节,废话不多说直接开撸. 首先简单说一下HashMap的实现原理: 首先有一个Node<k,v> ...

  8. 【ChatGPT】ChatGPT 原理全解析——读完这10篇论文,你就懂了。

    2022年11月,OpenAI推出人工智能聊天原型ChatGPT,再次赚足眼球,为AI界引发了类似AIGC让艺术家失业的大讨论. ChatGPT 是一种专注于对话生成的语言模型.它能够根据用户的文本输 ...

  9. MySQL 中事务的实现原理全解析

    在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性,而我们不知道的可能就是数据库是如何实现这四个属性的:在这篇文章中,我们将对事务的实现进行分析,尝 ...

  10. 电商行业智能搜索技术原理全解析

    简介: 对于电商平台来说,智能搜索功能是至关重要的.本文剖析电商行业的搜索专属特点和业务需求,并介绍开放搜索提供的[电商行业模板]智能搜索能力,希望带给企业更多提升业务转化的思路和解决方案~ 一.搜索 ...

最新文章

  1. 算法------设计哈希映射
  2. 1215.1——动态分配内存的补充realloc
  3. 洛谷1417烹调方案——动态规划:价值受时间影响
  4. 全球最大的披萨公司,在中国干不过必胜客?
  5. oracle 多表视图更新
  6. python彩票预测与分析_130期柳无尘双色球预测奖号:红球和值分析
  7. 基于MediaTek_ApSoC_SDK_4320_20150414 编译mt7621 的uboot
  8. BW数据加载后不能实时刷新到水晶易表解决方法
  9. Prioritizing Web Usability
  10. 可爱精灵宝贝 DP/爆搜
  11. 1999年中国省、自治区的城市规模结构特征的一些数据,可通过聚类分析将这些省、自治区进行分类_BeansSuperman_新浪博客
  12. Word处理控件Aspose.Words功能演示:使用Java合并MS Word文档
  13. 文件夹只读属性不能更改如何去除
  14. cleintHeight、offsetHeight、scrollHeight、cleintTop、offsetTop、scrollTop、getClientRects、getBoundingClien
  15. 什么是多进程-多线程-多协程 ----进程和多线程
  16. python蜂鸣器天空之城频率_用python来一首钢琴solo天空之城
  17. 分数指数幂计算机,分数指数幂教案
  18. PostgreSQL数据库自带的命令行工具--psql
  19. 疫情导致招聘平台Xing的母公司New Work SE裁员
  20. CAD怎么转换版本?两个办法解决

热门文章

  1. 用水浒传来学习OKR
  2. selenium:class属性内带有空格的定位坑
  3. 逻辑运算符,if、swtch语句(java基础知识三)
  4. 经典BBS语录2007贺岁版
  5. 饮食、生物钟、肠道菌群的“三角恋”
  6. Web 应用防火墙如何添加域名
  7. Unity-黑暗之魂复刻-角色攻击
  8. 2021美食林全球餐厅精选榜公布,这里有一份美食地图请查收!
  9. 梦熊杯-十二月月赛-白银组题解-D.智慧
  10. Python 基础 (-)