Android Jetpack LiveData解析

目前關于LiveData源碼解讀的文章非常多了掸犬,本文就不重復了侈净,這里只對核心流程做解讀。關于源碼流程抹腿,推薦:Android livedata 源碼解剖

系列文章

Android Jetpack ViewModel解析
Android Jetpack LiveData解析
Android Jetpack DataBinding原理淺析(簡版)

本文主要解決如下幾個問題:
1晕鹊、LiveData是如何關聯生命周期的松却?
2、LiveData的發(fā)送事件溅话、接收事件原理
3玻褪、為什么LiveData可以先發(fā)射數據再注冊?類似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來存放生命周期State寸潦、Event等。在執(zhí)行生命周期方法的時候更新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狀態(tài)易核。注意:STATE 為STARTED/RESUMED就是說生命周期的方法為ononStart()匈织、onResume()onPause()時都表示active,這3個狀態(tài)都可以接收數據更新呻纹。網上很多說只有onstart和onresume才是活躍狀態(tài)的應該都是沒有驗證過的缀踪。

onStateChanged
是GenericLifecycleObserver接口的方法,當生命周期變化的時候會回調它弦追。這里會做判斷,如果是已經destroy花竞,直接移除觀察者劲件;否則會調用 activeStateChanged 方法。

到這里约急,問題1--關聯生命周期就講清楚了零远。


二、LiveData的發(fā)送事件厌蔽、接收事件原理

LiveData發(fā)送事件有2個方法:setValue()和postValue()牵辣,其中setValue()方法只能在主線程發(fā),而postValue()方法可以在子線程發(fā)奴饮,Observer的onChange()始終在主線程纬向。

setValue

setValue

只要有出于active狀態(tài)的觀察者,這個方法就會被數據發(fā)送給它們戴卜。這里的關鍵是dispatchingValue(null);

dispatchingValue

按照上面的調用可以看出逾条,當參數為 null 的時候,遍歷所有的 obsever投剥,最后通過調用considerNotify()進行分發(fā)师脂。

considerNotify

這里會做一些判斷,只有出于活躍狀態(tài)且數據是數據是最新的江锨,才會去分發(fā)數據吃警,最后回調到我們熟悉的onChanged()

postValue

這個方法是可以在子線程中發(fā)數據的啄育,但是我們最后的onChanged()方法卻始終在主線程的汤徽,這是怎么做到的呢?答案也很簡單灸撰,就在postToMainThread()方法谒府,具體是在DefaultTaskExecutor#postToMainThread創(chuàng)建一個主線程的handler把任務發(fā)到主線程拼坎,最后還是調用setValue()

三完疫、為什么LiveData可以先發(fā)射數據再注冊泰鸡?類似EventBus的粘性消息

我們都知道LiveData在發(fā)射數據的時候,如果Activity出于inactive狀態(tài)時是接受不到消息的壳鹤,而是等變成active狀態(tài)時才能接受盛龄。而且還能實現類似EventBus的粘性消息,比如在A頁面發(fā)射數據芳誓,然后打開B頁面余舶,在B頁面注冊觀察者,同樣能接收到數據锹淌。它的原理其實不復雜的匿值,下面一起來看看:


setValue

在方法內部首先會保存要發(fā)射的數據,然后調用dispatchingValue分發(fā)數據赂摆,這個方法最后會調用considerNotify()方法

considerNotify

considerNotify()方法會先判斷觀察者是否處于active狀態(tài)挟憔,只有出于活躍狀態(tài)的觀察者才能收到消息。那為什么頁面從后臺切換到可見狀態(tài)也就是active時又能接收到之前發(fā)射的數據呢烟号?


原因很簡單绊谭,因為LiveData內部關聯了生命周期相關的方法,當生命周期變化的時候都會回調onStateChanged()方法汪拥,在這里面會去調用activeStateChanged(),而它最后會調用considerNotify()方法去分發(fā)消息达传。而粘性消息也是這個原理。


四迫筑、LiveData如何保證不會內存泄漏的宪赶?

在一開始的observe()方法中會把Activity中創(chuàng)建的Observer對象添加到一個Map集合中,最后當生命周期方法執(zhí)行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引用了疯潭,這樣就不會發(fā)生內存泄漏的情況了赊堪。

總結

最后借用 可被感知的數據 - LiveData 原理詳解一張圖來總結LiveData原理

LiveData原理

更多LiveData高階用法和思想會在后續(xù)文章發(fā)布,如果文章對您有所幫助竖哩,請點贊哭廉、關注!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末期丰,一起剝皮案震驚了整個濱河市群叶,隨后出現的幾起案子,更是在濱河造成了極大的恐慌钝荡,老刑警劉巖街立,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異埠通,居然都是意外死亡赎离,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門端辱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梁剔,“玉大人,你說我怎么就攤上這事舞蔽∪俨。” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵渗柿,是天一觀的道長个盆。 經常有香客問我,道長朵栖,這世上最難降的妖魔是什么颊亮? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮陨溅,結果婚禮上终惑,老公的妹妹穿的比我還像新娘。我一直安慰自己门扇,他們只是感情好雹有,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布偿渡。 她就那樣靜靜地躺著,像睡著了一般件舵。 火紅的嫁衣襯著肌膚如雪卸察。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天铅祸,我揣著相機與錄音坑质,去河邊找鬼。 笑死临梗,一個胖子當著我的面吹牛涡扼,可吹牛的內容都是我干的。 我是一名探鬼主播盟庞,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吃沪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了什猖?” 一聲冷哼從身側響起票彪,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤不狮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后推掸,有當地人在樹林里發(fā)現了一具尸體驻仅,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年毡泻,在試婚紗的時候發(fā)現自己被綠了仇味。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡邪铲,死狀恐怖芬位,靈堂內的尸體忽然破棺而出无拗,到底是詐尸還是另有隱情,我是刑警寧澤昧碉,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布英染,位于F島的核電站揽惹,受9級特大地震影響,放射性物質發(fā)生泄漏四康。R本人自食惡果不足惜搪搏,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闪金。 院中可真熱鬧疯溺,春花似錦、人聲如沸哎垦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漏设。三九已至墨闲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郑口,已是汗流浹背鸳碧。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留犬性,地道東北人瞻离。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像仔夺,于是被迫代替她去往敵國和親琐脏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359