LiveData
本身很簡單,但其代表卻正是 MVVM模式最重要的思想,即 數據驅動視圖(也有叫觀察者模式、響應式等)——這也是擺脫 順序性編程思維的重要一步。
回顧LiveData:從處境尷尬到鹹魚翻身
我們都知道Google在去年的 I/O 大會非常隆重地推出了一系列的 架構組件,本文的主角,LiveData正是其中之一,和Lifecycle
、ViewModel
、Room
比較起來,LiveData
可以說是最受關注的組件也不爲過,遺憾的是,在發佈的最初,關注點是因爲它飽含爭議,相當一部分的開發者認爲——LiveData
實在太 雞肋了!
2017年的 Android
技術領域,RxJava
無疑是炙手可熱的名詞之一,其 觀察者模式和 鏈式調用所表現出來的 API 優秀地設計,使得它位於很多 Android項目技術選型中的 第一序列。
這時 Google 隆重推出了具有類似功能的 LiveData
(其本質就是觀察者模式),可以說是有點初生牛犢不怕虎的感覺,開發者們不由自主將LiveData
和 RxJava
進行了對比,結論基本出奇的一致—— LiveData
所提供的功能,RxJava
完全足以勝任,而後者卻同時具有龐大的生態圈,這是LiveData
短時間內難以撼動(替代)的。
時至今日,LiveData
的使用者越來越多,最主要的原因當然和Google的強力支持不無關係,但是LiveData
本身優秀的設計和輕量級也吸引了越來越多開發者的青睞。
現在我們需要去了解它了,我們都知道,LiveData
本質是 觀察者模式的體現,可關鍵的問題是:
觀察者模式到底是啥?!
討論這個問題之前,我們先看看 LiveData
的用法,這實在沒什麼技術難度,比如,你可以這樣實例化一個LiveData
並使用它:
如你所見,LiveData
實際上就像一個 容器, 本文中它存儲了一個String
類型的引用,每當這個容器內 String
的數據發生變化,我們都能在回調函數中進行對應的處理,比如 Toast。
這似乎和我們日常用到的 Button
控件的 setOnClickListener()
非常相似,實際上點擊事件的監聽也正是 觀察者模式的一種體現,對於觀察者來說,它並不關心觀察對象 數據是如何過來的,而只關心數據過來後 進行怎樣的處理。
這也就是說,事件發射的上游和 接收事件的下游互不干涉,大幅降低了互相持有的依賴關係所帶來的強耦合性。
我依然堅持學習原理比學習如何應用的優先級更高,因此我們先來一一探究LiveData
本身設計中存在的那些閃光點背後的故事。
LiveData是如何避免內存泄漏的
我們都知道,RxJava
在使用過程中,避免內存泄漏是一個不可忽視的問題,因此我們一般需要藉助三方庫比如RxLifecycle
、AutoDispose
來解決這個問題。
而反觀LiveData
,當它被我們的Activity
訂閱觀察,這之後Activity
如果finish()
掉,LiveData
本身會自動“清理”以避免內存泄漏。
這是一個非常好用的特性,它的實現原理非常簡單,其本質就是利用了Jetpack 架構組件中的另外一個成員—— Lifecycle。
讓我們來看看LiveData
被訂閱時內部的代碼:
源碼中的邏輯非常複雜,我們只關注核心代碼:
- 1.首先我們在調用
LiveData.observer()
方法時,傳遞的第一個參數Acitivity
實際被向上抽象成爲了LifecycleOwner
,第二個參數Obserser
實際就是我們的觀察後的回調。
這裏我們需要注意的是,執行
LiveData.observer()
方法時 必須處於主線程,否則會因爲斷言失敗而拋出異常。
- 2.方法內部實際上將我們傳入的2個參數包裝成了一個新的
LifecycleBoundObserver
對象,它實現了 Lifecycle組件中的LifecycleObserver
接口:
這裏就解釋了爲什麼LiveData
能夠 自動解除訂閱而避免內存泄漏了,因爲它內部能夠感應到Activity
或者Fragment
的生命週期。
這種設計非常巧妙——在我們初識 Lifecycle組件時,總是下意識認爲它能夠對大的對象進行有效生命週期的管理(比如 Presenter),實際上,這種生命週期的管理我們完全可以應用到各個功能的基礎組件中,比如大到吃內存的 MediaPlayer(多媒體播放器)、繪製設計複雜的 自定義View,小到隨處可見的LiveData
,都可以通過實現LifecycleObserver
接口達到 感應生命週期並內部釋放重的資源的目的。
關於上述代碼中註釋了 更新LiveData的活躍狀態的源碼,我們先跳過,稍後我們會詳細探討它。
-
- 我們繼續回到上上一個源碼片段的第三步中,對於一個可觀察的
LiveData
來講,當然存在多個觀察者同時訂閱觀察的情況,因此考慮到這一點,Google的工程師們爲每一個LiveData
配置了一個Map
存儲所有的觀察者。
- 我們繼續回到上上一個源碼片段的第三步中,對於一個可觀察的
-
4.到了這一步,我們將第2步包裝生成的對象交給我們傳入的
Activity
,讓它在不同的生命週期事件中去逐一通知其所有的觀察者,當然也包含了我們的LiveData
。
數據更新後如何通知到回調方法?
LiveData
原生的API提供了2種方式供開發者更新數據, 分別是 setValue()
和postValue()
,官方文檔明確標明:setValue()
方法必須在 主線程進行調用,而postValue()
方法更適合在執行較重工作 子線程中進行調用(比如網絡請求等)——在所有情況下,調用setValue()
或postValue()
都會 觸發觀察者並更新UI。
柿子挑軟的捏,我們先看setValue()
方法的實現原理:
通過保留最終的核心代碼,我們很清晰瞭解了setValue()
方法爲什麼能更新LiveData
的值,並且通知到回調函數中的代碼去執行,比如更新UI。
但是我們知道,普遍情況下,Android不允許在子線程更新UI,但是postValue()
方法卻可以在子線程更新LiveData()
的數據,並通知更新UI,這是如何實現的呢?
其實答案已經呼之欲出了,就是通過 Handler
:
現在你已經對LiveData
整體了一個基本的瞭解了,接下來讓我們開始去探究更細節的閃光點。
看完源碼,你告訴我纔算入門?
LiveData
本身非常簡單,畢竟它本身的源碼一共也就500行左右,也許你要說 準備面試粗讀一遍源碼就夠了,很遺憾,即使是粗讀了源碼,也很難說能夠完全招架更深入的提問...
讓我們來看一道題目:在下述Activity完整的生命週期中,Activity
一共觀察到了幾次數據的變更——即 一共打印了幾條Log?
公佈答案:
意外的是,livedata.observer()
的本次觀察並沒有觀察到 onCreate、onStop和 onDestroy的數據變更。爲什麼會這樣?
還記得上文提到過2次的 LiveData的活躍狀態(Active)相關代碼嗎?實際上,LiveData
內部存儲的每一個LifecycleBoundObserver
本身都有shouldBeActive
的狀態:
現在我們明白了,原來並不是只要在onDestroy()
之前爲LiveData
進行更新操作,LiveData
的觀察者就能響應到對應的事件的。
雖然我們明白了這一點,但是如果更深入的思考,你會又多一個問題,那就是:
- 既然
LiveData
已經能夠實現在onDestroy()
的生命週期時自動解除訂閱,爲什麼還要多此一舉設置一個Active
的狀態呢?
仔細想想,其實也不難得到答案,Activity
並非只有onDestroy()
一種狀態的,更多時候,新的Activity
運行在棧頂,舊的Activity
就會運行在 background
——這時舊的Activity
會執行對應的onPause()
和onStop()
方法,我們當然不會關心運行在後臺的Activity
所觀察的LiveData
對象(即使數據更新了,我們也無從進行對應UI的更新操作),因此LiveData
進入 **InActive(待定、非活躍)**狀態,return
並且不去執行對應的回調方法,是 非常縝密的優秀設計。
當然,有同學提出,我如果希望這種情況下,Activity
在後臺依然能夠響應數據的變更,可不可以呢?當然可以,LiveData
此外還提供了observerForever()
方法,在這種情況下,它能夠響應到任何生命週期中數據的變更事件:
除此之外,源碼中處處都是優秀的細節,比如對於observe()
方法和observerForever()
方法對應生成的包裝類,後者方法生成的是AlwaysActiveObserver
對象,統一抽象爲ObserverWrapper
。
這種即使只有2種不同場景,也通過代碼的設計,將公共業務進行向上抽離爲抽象類的嚴謹,也非常值得我們學習。
小結,與更深入的思考
本來寫了更多,篇幅所限,最終還是決定刪除了相當一部分和 RxJava
有關的內容,這些內容並非是將 LiveData
和 RxJava
進行對比一決高下—— 例如,Google官方提供了 LiveData
和 RxJava
互相進行轉換的工具類:
developer.android.com/reference/a…
值得玩味的是,官方的工具類中,LiveData
向RxJava
的轉換方法,返回值並非是一個Flowable
,而是一個Publisher
接口:
正如我在註釋中標註的,這個工具方法返回的是一個接口,很大程度上限制了我們對RxJava
衆多強大操作符的使用,這是否是來自Google的惡意?
當然不是,對於這種行爲,我的理解是Google對於LiveData
本身嚴格的約束——它只應該用於進行數據的觀察,而不是花哨的操作;轉換爲Flowable當然非常簡單,但是這種行爲是否屬於LiveData
本身職責的逾越,更準確來說,是否屬於不必要的過度設計?這些是我們需要去細細揣度的。
我無從驗證我的理解是否正確,但是我的這個理由已經足夠說服我自己,再往下已不再是LiveData
的範疇,關於這一點我將會專門起一篇文章去進行更深入的探討,歡迎關注。
作者:卻把清梅嗅
鏈接:https://juejin.im/post/5c25753af265da61561f5335