Android Jetpack LiveData解析

目前关于LiveData源码解读的文章非常多了,本文就不重复了,这里只对核心流程做解读。关于源码流程,推荐:Android livedata 源码解剖

系列文章

Android Jetpack ViewModel解析
Android Jetpack LiveData解析
Android Jetpack DataBinding原理浅析(简版)

本文主要解决如下几个问题:
1、LiveData是如何关联生命周期的?
2、LiveData的发送事件、接收事件原理
3、为什么LiveData可以先发射数据再注册?类似EventBus的粘性消息
4、LiveData如何保证不会内存泄漏的?

先看看LiveData的基本使用

MutableLiveData<String> mLiveData = mTestViewModel.getLiveData();
mLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
       //更新UI
    }
});

一、LiveData是如何关联生命周期的?

observe

先来看一下观察者订阅的方法observe(),第一参数是LifecycleOwner,第二个参数是观察者,也是最后接收回调的地方。如果你看过Lifecycle的的话,你应该知道,

关于LifecycleOwner
通常第一个参数传入Activity/Fragment对象,原因是新版本(>26)的FragmentActivityFragment已经实现了LifecycleOwner接口,而且它们内部有一个LifecycleRegistry来存放生命周期StateEvent等。在执行生命周期方法的时候更新LifecycleRegistry里的生命周期State、Event,,并且通过handleLifecycleEvent()方法来通知对应的观察者。

这个observe方法重点关注2点:
1、ObserverWrapper:通过LifecycleBoundObserver把observer包装;
2、addObserver():把包装后的observer对象添加到一个Map中,然后当LifecycleOwner改变的时候就会收到通知。

LifecycleBoundObserver

LifecycleBoundObserver

这里的关键方法是onStateChangedshouldBeActive

shouldBeActive
这里是调用了lifecycle的方法,如果Lifecycle 的 STATE 为STARTED/RESUMED则返回true,表示active状态。注意:STATE 为STARTED/RESUMED就是说生命周期的方法为ononStart()onResume()onPause()时都表示active,这3个状态都可以接收数据更新。网上很多说只有onstart和onresume才是活跃状态的应该都是没有验证过的。

onStateChanged
是GenericLifecycleObserver接口的方法,当生命周期变化的时候会回调它。这里会做判断,如果是已经destroy,直接移除观察者;否则会调用 activeStateChanged 方法。

到这里,问题1--关联生命周期就讲清楚了。


二、LiveData的发送事件、接收事件原理

LiveData发送事件有2个方法:setValue()和postValue(),其中setValue()方法只能在主线程发,而postValue()方法可以在子线程发,Observer的onChange()始终在主线程。

setValue

setValue

只要有出于active状态的观察者,这个方法就会被数据发送给它们。这里的关键是dispatchingValue(null);

dispatchingValue

按照上面的调用可以看出,当参数为 null 的时候,遍历所有的 obsever,最后通过调用considerNotify()进行分发。

considerNotify

这里会做一些判断,只有出于活跃状态且数据是数据是最新的,才会去分发数据,最后回调到我们熟悉的onChanged()

postValue

这个方法是可以在子线程中发数据的,但是我们最后的onChanged()方法却始终在主线程的,这是怎么做到的呢?答案也很简单,就在postToMainThread()方法,具体是在DefaultTaskExecutor#postToMainThread创建一个主线程的handler把任务发到主线程,最后还是调用setValue()

三、为什么LiveData可以先发射数据再注册?类似EventBus的粘性消息

我们都知道LiveData在发射数据的时候,如果Activity出于inactive状态时是接受不到消息的,而是等变成active状态时才能接受。而且还能实现类似EventBus的粘性消息,比如在A页面发射数据,然后打开B页面,在B页面注册观察者,同样能接收到数据。它的原理其实不复杂的,下面一起来看看:


setValue

在方法内部首先会保存要发射的数据,然后调用dispatchingValue分发数据,这个方法最后会调用considerNotify()方法

considerNotify

considerNotify()方法会先判断观察者是否处于active状态,只有出于活跃状态的观察者才能收到消息。那为什么页面从后台切换到可见状态也就是active时又能接收到之前发射的数据呢?


原因很简单,因为LiveData内部关联了生命周期相关的方法,当生命周期变化的时候都会回调onStateChanged()方法,在这里面会去调用activeStateChanged(),而它最后会调用considerNotify()方法去分发消息。而粘性消息也是这个原理。


四、LiveData如何保证不会内存泄漏的?

在一开始的observe()方法中会把Activity中创建的Observer对象添加到一个Map集合中,最后当生命周期方法执行ondestory之后会移除观察者,这样就避免了内存泄漏。从这段源码可以看出,最后是通过调用LifecycleBoundObserver#detachObserver方法来解绑观察者的。

你以为这样就完了?其实没完。我一开始看别人这样说的,就没去多想,后面仔细想想内存泄漏到本质觉得没有彻底搞清楚,于是有了下面的内容。

内存泄漏的本质是什么?

我所理解的是:当不再使用的对象被其它对象所引用,导致它无法被回收,就会造成内存泄漏。

下面来看看这里的引用关系:

MutableLiveData<String> mLiveData = mTestViewModel.getLiveData();
mLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
       //更新UI
    }
});

在使用的时候,observe方法第一个参数传入的是Activity/Fragment对象,这里Activity和LiveData是互相引用?这里是不完全对的,


observe

我们可以看到第一个参数owner最终是传入到LiveData内部类LifecycleBoundObserver中了,所以引用关系应该是
Activity<---->LiveData.LifecycleBoundObserver,所以最好移除了observer对象的时候,就不再持有Activity引用了,这样就不会发生内存泄漏的情况了。

总结

最后借用 可被感知的数据 - LiveData 原理详解一张图来总结LiveData原理

LiveData原理

更多LiveData高阶用法和思想会在后续文章发布,如果文章对您有所帮助,请点赞、关注!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342