目录介绍

  • 01.LiveData是什么东西
  • 02.使用LiveData的优势
  • 03.使用LiveData的步骤
  • 04.简单使用LiveData
  • 05.observe()和observerForever()
  • 06.LiveData原理介绍
  • 07.observe订阅源码分析
  • 08.setValue发送源码分析
  • 09.observeForever源码
  • 10.LiveData源码总结

00.使用LiveData实现bus事件总线

  • 利用LiveData实现事件总线,替代EventBus。充分利用了生命周期感知功能,可以在activities, fragments, 或者 services生命周期是活跃状态时更新这些组件。支持发送普通事件,也可以发送粘性事件;还可以发送延迟消息,以及轮训延迟消息等等。
  • https://github.com/yangchong211/YCLiveDataBus

01.LiveData是什么东西

  • 基于观察者模式

    • LiveData是一种持有可被观察数据的类。LiveData需要一个观察者对象,一般是Observer类的具体实现。当观察者的生命周期处于STARTED或RESUMED状态时,LiveData会通知观察者数据变化。
  • 感知生命周期
    • 和其他可被观察的类不同的是,LiveData是有生命周期感知能力的,这意味着它可以在activities, fragments, 或者 services生命周期是活跃状态时更新这些组件。那么什么是活跃状态呢?就是STARTED和RESUMED就是活跃状态,只有在这两个状态下LiveData是会通知数据变化的。
  • 自动解除数据订阅
    • 要想使用LiveData(或者这种有可被观察数据能力的类)就必须配合实现了LifecycleOwner的对象使用。在这种情况下,当对应的生命周期对象DESTORY时,才能移除观察者。这对Activity或者Fragment来说显得尤为重要,因为他们可以在生命周期结束的时候立刻解除对数据的订阅,从而避免内存泄漏等问题。

02.使用LiveData的优势

2.1 具有很明显的优点

  • UI和实时数据保持一致

    • 因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。
  • 不会发生内存泄露
    • 观察者被绑定到组件的生命周期上,当被绑定的组件销毁(onDestroy)时,观察者会立刻自动清理自身的数据。
  • 不会再产生由于Activity处于stop状态而引起的崩溃
    • 例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。
  • 不需要再解决生命周期带来的问题
    • LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。
  • 实时数据刷新
    • 当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据
  • 解决Configuration Change问题
    • 在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。
  • 数据共享
    • 如果对应的LiveData是单例的话,就能在app的组件间分享数据。这部分详细的信息可以参考继承LiveData

2.2 细节点补充

  • 组件和数据相关的内容能实时更新,组件在前台的时候能够实时收到数据改变的通知,当组件从后台到前台来时,LiveData能够将最新的数据通知组件,因此保证了组件中和数据相关的内容能够实时更新。
  • 如果横竖屏切换(configuration change)时,不需要额外的处理来保存数据,当屏幕方向变化时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。

03.使用LiveData的步骤

  • 创建一个持有某种数据类型的LiveData (通常是在ViewModel中)
  • 创建一个定义了onChange()方法的观察者。这个方法是控制LiveData中数据发生变化时,采取什么措施 (比如更新界面)。通常是在UI Controller (Activity/Fragment) 中创建这个观察者。
  • 通过 observe()方法连接观察者和LiveData。observe()方法需要携带一个LifecycleOwner类。这样就可以让观察者订阅LiveData中的数据,实现实时更新。

04.简单使用LiveData

4.1 单独使用LiveData

  • 举一个最简单的案例代码:

  • 那么上面这一段代码大概是什么意思呢?
    • 首先创建一个 MutableLiveData(LiveData是抽象类)对象 ,通过 observe 方法可以订阅修改数据的通知,通过 postValue()或者 setValue() 方法发送事件更新数据,已经订阅的 Observer 能够得到数据更改的通知,就会回调 onChanged() 方法。

4.2 使用LiveData配合ViewModel

  • LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合。它是一个抽象类,首先先创建一个类实现LiveData。代码如下所示:

  • 创建一个观察的对象,观察LiveData中的数据。目前在组件的onCreate()方法中开始观察数据,代码如下所示:
    • 思考下,可以在onResume()中调用么,个人觉得不太好。因为系统会多次调用onResume()方法。
  • 然后去创建更新对象数据内容的对象。如何去更新那个文本中的数据呢?代码如下所示:
    • 想要在UI Controller中改变LiveData中的值呢?(比如点击某个Button设置文本内容的更改)。
    • LiveData并没有提供这样的功能,但是Architecture Component提供了MutableLiveData这样一个类,可以通过setValue(T)和postValue(T)方法来修改存储在LiveData中的数据。MutableLiveData是LiveData的一个子类,从名称上也能看出这个类的作用。
    • 调用setValue()方法就可以把LiveData中的值改为 "小杨真的是一个逗比么" 。同样,通过这种方法修改LiveData中的值同样会触发所有对这个数据感兴趣的类。那么setValue()和postValue()有什么不同呢?区别就是setValue()只能在主线程中调用,而postValue()可以在子线程中调用。

05.observe()和observerForever()

  • 一般我们使用 LiveData 的 observe(),当数据更新后,LiveData 会通知它的所有活跃的观察者。

    • 与 RxJava 不同的,LiveData 只会通知活跃的观察者,例如 Activity 位于 Destroyed 状态时是不活跃的,因此不会收到通知。
  • 当然我们也可以使用 LiveData 的 observerForever() 方法进行订阅,区别是 observerForever() 不会受到 Activity 等组件的生命周期的影响,只要数据更新就会收到通知。

06.LiveData原理介绍

6.1 简单的原理介绍

  • LiveData可对数据进行观测, 并具有生命周期感知能力, 这就意味着当liveData只会在生命周期处于活跃(inActive)的状态下才会去执行观测动作, 而他的能力赋予不能脱离LifeCycle的范围。
  • 需要注意的是,LiveData内维护的mVersion表示的是发送信息的版本,每次发送一次信息, 它都会+1, 而ObserverWrapper内维护的mLastVersion为订阅触发的版本号, 当订阅动作生效的时候, 它的版本号会和发送信息的版本号同步.他们初始值都为-1。

6.2 然后思考一些问题

  • a.liveData如何实现订阅者模式,如何处理发送事件?
  • b.如何做到感知生命周期的,怎么跟 LifecycleOwner 进行绑定的?
  • c.LiveData 只在 LifecycleOwner active 状态发送通知,是怎么处理的?
  • d.LiveData 会自动在 DESTROY 的状态下取消订阅,是怎么处理的?
  • e.生命周期变化后数据处理流程是怎么样的?
  • f.为什么观察者只能与一个LifecycleOwner绑定,而不是多个?

07.observe订阅源码分析

7.1 首先看看observe方法源码

  • 直接查看源代码,如下所示:

    • 当前绑定的组件(activity或者fragment)状态为DESTROYED的时候, 则会忽视当前的订阅请求,也就是忽略owner的注册;
    • 如果需要与生命周期绑定, 则需要传入LifecycleOwner对象, 将我们的LiveData数据观测者(Observer)包装注册到生命周期的观测者中, 就是源码中创建wrapper对象过程;
    • 需要注意的问题是,不能添加具有不同生命周期的相同观察者,否则就会抛出IllegalArgumentException异常,但是owner可以add多个Observer;
    • 最后添加一个LifecycleObserver,它将在LifecycleOwner更改状态时得到通知,并做出及时的对应更新活动。

7.2 看看LifecycleBoundObserver源码

  • 然后看一下观察者类LifecycleBoundObserver的源代码

    • LifecycleBoundObserver对象, 它继承于ObserverWrapper, 并最终实现了GenericLifecycleObserver接口;
    • 在发生状态转换事件时,会调用onStateChanged方法,在这个方法中,如果是DESTROYED状态,则先要移除观察者,然后在取到生命周期状态变更事件
  • 通过上面的代码可以发现什么?
    • GenericLifecycleObserver是一个接口,ObserverWrapper是一个抽象类,而LifecycleBoundObserver则是ObserverWrapper的子类,并且重写了其中几个方法;
    • 在LifecycleBoundObserver的shouldBeActive()方法,在 owner 处于至少是 STARTED 的状态下认为是 active 状态;
    • 而且它也实现了 GenericLifecycleObserver 接口,可以监听 lifecycle 回调。在 onStateChanged() 方法里处理了生命周期改变的事件,在这个方法中,当接收到 DESTROYED 的事件会自动解除跟 owner 的绑定;
    • 将下个流程交给了 activeStateChanged(),这里具体可以看抽象类ObserverWrapper中的activeStateChanged源码;
  • 看一下ObserverWrapper抽象类中activeStateChanged方法中,onActive和onInactive分别干什么呢?
    • 对于onActive方法,当活动观察者的数量从0变为1时调用;对于onInactive方法,当活动观察者的数量从1变为0时调用
  • 看一下ObserverWrapper抽象类中activeStateChanged方法中,dispatchingValue是干什么呢?
    • 这个方法在分析下面setValue源码时还会说到,具体看下面的介绍!

7.3 看看mObservers.putIfAbsent操作

  • 关于observe源码中这一行代码ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper)作用是什么呢?

    • mObservers.putIfAbsent(observer, wrapper)存入容器中,mObservers.putIfAbsent这个添加数据的方式比较少见。
    • 看了下面源代码可知,支持键值对存储,用链表实现,不是线程安全的。既然这里有存数据,那肯定有地方会取数据用到,这个后面会说到……

7.4 注册观察者流程

  • 那么注册观察者之后的触发流程是怎样的?

    • 调用 observe() 注册后,由于绑定了 owner,所以在 active 的情况下,使用LiveData中setValue发送数据,则 Observer 会立马接受到该数据修改的通知。
    • observe ——> onStateChanged ——> activeStateChanged ——> dispatchingValue ——> considerNotify ——> onChanged
    • 至于最终走到了onChanged方法,这个方法则是交给外部开发者处理接收消息事件的逻辑

08.setValue发送源码分析

8.1 setValue源码分析

  • LiveData 更新数据方式有两个,一个是 setValue() 另一个是 postValue(),这两个方法的区别是,postValue() 在内部会抛到主线程去执行更新数据,因此适合在子线程中使用;而 setValue() 则是直接更新数据。

  • 跟进下 dispatchingValue() 方法,注意,这里需要重点看considerNotify代码:
  • 接下来看一下上面源码中initiator对象为空判断逻辑区别
    • dispatchingValue 这里分两种情况:ObserverWrapper不为null和ObserverWrapper为null
    • ObserverWrapper不为null 的情况。LifecycleBoundObserver.onStateChanged 方法里调用了 activeStateChanged ,而该方法调用dispatchingValue(this);传入了 this ,也就是 LifecycleBoundObserver ,这时候不为 null 。也就是说生命周期改变触发的流程就是这种情况,这种情况下,只会通知跟该 Owner 绑定的 Observer。
    • ObserverWrapper为null 的情况。经过分析发现在setValue方法中调用dispatchingValue(null)传递了空对象,这个时候的流程则会通知 active 的mObservers

8.2 看一下considerNotify()做什么

  • 然后看一下considerNotify() 方法做了什么,代码如下所示,这里有道词典翻译下注释

    • 如果ObserverWrapper的mLastVersion小于LiveData的mVersion,就会去回调mObserver的onChanged方法。
    • 每个新的订阅者,其version都是-1,LiveData一旦设置过其version是大于-1的(每次LiveData设置值都会使其version加1),这样就会导致LiveDataBus每注册一个新的订阅者,这个订阅者立刻会收到一个回调,即使这个设置的动作发生在订阅之前。
  • 思考一下dispatchingValue除了setValue会调用,其他还有地方调用么?
    • dispatchingValue除了在我们主动更新数据的时候会触发,
    • 当LifeCircleOwner的状态发生变化的时候,会调用LiveData.ObserverWrapper的activeStateChanged函数。
    • 在我们的观察者状态变更(inactive->active)的时候, 也会通知到, 这就导致了LiveData必然支持粘性事件。
    • 如果这个时候ObserverWrapper的状态是active,就会调用LiveData的dispatchingValue。
    • 它主要是处理分发通知逻辑,并且在分发通知前会判断 owner 的状态,再加上 LiveData 本身内部的版本管理,确保了只会发送最新的数据给 active 状态下的 Observer。
    • LiveData 对同时多次修改数据做了处理,如果同时多次修改,只会修改为最新的数据。

8.3 发送消息事件流程

  • 那么发送消息事件之后的触发流程是怎样的?

    • setValue ——> dispatchingValue(null) ——> considerNotify(注意,这里是个for迭代器循环,表示通知所有观察者) ——> onChanged

09.observeForever源码

  • 这个方法是干什么用的呢?看一下源代码

    • 将给定的观察者添加到观察者列表中,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知

10.LiveData源码总结

  • LiveData的观察者可以联动生命周期, 也可以不联动。在联动生命周期时,会自动在 DESTROYED 的状态下移除 Observer ,取消订阅,所以不用担心内存泄露;
  • LiveData的观察者只能与一个LifecycleOwner绑定, 否则会抛出异常。而一个 owner 可以绑定多个 Observer 实例;
  • LiveData 跟 LifecycleOwner 绑定,能感知生命周期变化,并且只会在 LifecycleOwner 处于 Active 状态(STARTED/RESUMED)下通知数据改变;如果数据改变发生在非 active 状态,数据会变化,但是不发送通知,等 owner 回到 active 的状态下,再发送通知;
  • 使用observeForever()方法,会注意AlwaysActiveObserver对象,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知
  • LiveData 利用版本管理、绑定 Lifecycle 确保了只会发送最新的数据给 active 状态下的 Observer

参考博客

开源LiveData事件总线:https://github.com/yangchong211/YCLiveDataBus

LiveData详细分析的更多相关文章

  1. ZIP压缩算法详细分析及解压实例解释

    最近自己实现了一个ZIP压缩数据的解压程序,觉得有必要把ZIP压缩格式进行一下详细总结,数据压缩是一门通信原理和计算机科学都会涉及到的学科,在通信原理中,一般称为信源编码,在计算机科学里,一般称为数据 ...

  2. 1125MySQL Sending data导致查询很慢的问题详细分析

    -- 问题1 tablename使用主键索引反而比idx_ref_id慢的原因EXPLAIN SELECT SQL_NO_CACHE COUNT(id) FROM dbname.tbname FORC ...

  3. LinkedList详细分析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  4. android ListView 九大重要属性详细分析、

    android ListView 九大重要属性详细分析. 1.android ListView 一些重要属性详解,兄弟朋友可以参考一下. 首先是stackFromBottom属性,这只该属性之后你做好 ...

  5. C语言中的static 详细分析

    转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...

  6. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  7. Android-Native-Server 启动和注册详细分析

    Android-Native-Server 启动和注册详细分析     以mediaService为实例来讲解: mediaService的启动入口 是一个 传统的  main()函数 源码位置E:\ ...

  8. px,dp,dip,sp,in,mm,pt详细分析

    px,dp,dip,sp,in,mm,pt详细分析 px   :(pixels),屏幕的像素点,不同的设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多. dip  :(devi ...

  9. Http Pipeline详细分析(下)

    Http Pipeline详细分析(下) 文章内容 接上面的章节,我们这篇要讲解的是Pipeline是执行的各种事件,我们知道,在自定义的HttpModule的Init方法里,我们可以添加自己的事件, ...

  10. 用户代理检测与浏览器Ua详细分析

    用户代理检测与浏览器Ua详细分析:http://www.cnblogs.com/hykun/p/Ua.html

随机推荐

  1. 五一不休息,每天都学习,从零教你手写节流throttle

    壹 ❀ 引 我在 从零教你手写实现一个防抖debounce方法 一文中详细的介绍了防抖概念,以及如何手写一个防抖.既然聊到防抖那自然避不开同等重要的节流throttle,老规矩,我们先阐述节流的概念, ...

  2. 延时队列 DelayQueue

    当用户超时未支付时,给用户发提醒消息.另一种场景是,超时未付款,订单自动取消.通常,订单创建的时候可以向延迟队列种插入一条消息,到时间自动执行.其实,也可以用临时表,把这些未支付的订单放到一个临时表中 ...

  3. Elasticsearch Web管理工具

    Cerebro是一个开源的elasticsearch web管理工具 首先,下载Elasticsearch https://www.elastic.co/guide/en/elasticsearch/ ...

  4. AsyncHttpClient And Download Speed Limit

    AsyncHttpClient Official repository and docs: https://github.com/AsyncHttpClient/async-http-client M ...

  5. STC89C52控制74HC595,74HC138双色16x16点阵屏循环显示汉字

    简介 常见的LED点阵除了使用MAX7219, 还有一部分是使用74HC595, 前者能主动刷新, 后者需要上位机主动扫描刷新. 手里这块是德飞莱的16x16LED点阵模块, 板上印的型号LY-LED ...

  6. LTspice XVII使用笔记

    安装 前往官网下载 https://www.analog.com/cn/design-center/design-tools-and-calculators/ltspice-simulator.htm ...

  7. 【Unity3D】绘制物体外框线条盒子

    1 需求描述 ​ 点选物体.框选物体.绘制外边框 中介绍了物体投影到屏幕上的二维外框绘制方法,本文将介绍物体外框线条盒子绘制方法. 内框:选中物体后,绘制物体的内框(紧贴物体.并与物体姿态一致的内框盒 ...

  8. 使用UTL_HTTP包获取网页内容

    UTL_HTTP 包提供了容易的方式通过HTTP协议获取网页内容,下面结合几个例子介绍一下: ----------------------------------------------------- ...

  9. Java Console类

    用于从控制台设备读取字符信息,通常是文本和密码.尤其读取密码字符时是看不见的. 下面给出一个例子: import java.io.Console; /** * @author xusucheng * ...

  10. 在Windows10中安装解压版MySQL 8.X

    在Windows 10中安装解压版的MySQL 8.X实现步骤: 1.下载MySQL安装包:https://dev.mysql.com/downloads/mysql/ 解压到指定目录,比如:D:\o ...