Android官方架構組件LiveData: 觀察者模式領域二三事

本文是 《Android Jetpack 官方架構組件》 系列文章, LiveData自己很簡單,但其表明卻正是 MVVM 模式最重要的思想,即 數據驅動視圖(也有叫觀察者模式、響應式等)——這也是擺脫 順序性編程思惟 的重要一步。android

回顧LiveData:從處境尷尬到鹹魚翻身

咱們都知道Google在去年的 I/O 大會很是隆重地推出了一系列的 架構組件,本文的主角,LiveData 正是其中之一,和LifecycleViewModelRoom比較起來,LiveData能夠說是最受關注的組件也不爲過,遺憾的是,在發佈的最初,關注點是由於它飽含爭議,至關一部分的開發者認爲——LiveData 實在太 雞肋 了!git

2017年的 Android 技術領域,RxJava無疑是煊赫一時的名詞之一,其 觀察者模式鏈式調用 所表現出來的 API 優秀地設計,使得它位於不少 Android項目技術選型中的 第一序列github

這時 Google 隆重推出了具備相似功能的 LiveData (其本質就是觀察者模式),能夠說是有點初生牛犢不怕虎的感受,開發者們情不自禁將LiveDataRxJava 進行了對比,結論基本出奇的一致—— LiveData所提供的功能,RxJava徹底足以勝任,然後者卻同時具備龐大的生態圈,這是LiveData短期內難以撼動(替代)的。面試

時至今日,LiveData的使用者愈來愈多,最主要的緣由固然和Google的強力支持不無關係,可是LiveData自己優秀的設計和輕量級也吸引了愈來愈多開發者的青睞。編程

如今咱們須要去了解它了,咱們都知道,LiveData 本質是 觀察者模式 的體現,可關鍵的問題是:網絡

觀察者模式究竟是啥?!

討論這個問題以前,咱們先看看 LiveData 的用法,這實在沒什麼技術難度,好比,你能夠這樣實例化一個LiveData並使用它:架構

爲了保證代碼簡潔可讀,示例代碼我使用了Kotlin

如你所見,LiveData實際上就像一個 容器, 本文中它存儲了一個String類型的引用,每當這個容器內 String的數據發生變化,咱們都能在回調函數中進行對應的處理,好比 Toastapp

這彷佛和咱們平常用到的 Button 控件的 setOnClickListener() 很是類似,實際上點擊事件的監聽也正是 觀察者模式 的一種體現,對於觀察者來講,它並不關心觀察對象 數據是如何過來的,而只關心數據過來後 進行怎樣的處理框架

這也就是說,事件發射的上游接收事件的下游 互不干涉,大幅下降了互相持有的依賴關係所帶來的強耦合性。函數

我依然堅持學習原理比學習如何應用的優先級更高,所以咱們先來一一探究LiveData自己設計中存在的那些閃光點背後的故事。

LiveData是如何避免內存泄漏的

咱們都知道,RxJava在使用過程當中,避免內存泄漏是一個不可忽視的問題,所以咱們通常須要藉助三方庫好比RxLifecycleAutoDispose來解決這個問題。

而反觀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的活躍狀態 的源碼,咱們先跳過,稍後咱們會詳細探討它。

    1. 咱們繼續回到上上一個源碼片斷的第三步中,對於一個可觀察的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 ?(補充糾正,onStop()方法中值應該爲 "onStop")

公佈答案:

意外的是,livedata.observer()的本次觀察並無觀察到 onCreateonStoponDestroy 的數據變動。

爲何會這樣?

還記得上文提到過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 有關的內容,這些內容並不是是將 LiveDataRxJava 進行對比一決高下—— 例如,Google官方提供了 LiveDataRxJava 互相進行轉換的工具類:

developer.android.com/reference/a…

值得玩味的是,官方的工具類中,LiveDataRxJava的轉換方法,返回值並不是是一個Flowable,而是一個Publisher接口:

正如我在註釋中標註的,這個工具方法返回的是一個接口,很大程度上限制了咱們對RxJava衆多強大操做符的使用,這是不是來自Google的惡意

固然不是,對於這種行爲,個人理解是Google對於LiveData自己嚴格的約束——它只應該用於進行數據的觀察,而不是花哨的操做;轉換爲Flowable固然很是簡單,可是這種行爲是否屬於LiveData自己職責的逾越,更準確來講,是否屬於沒必要要的過分設計?這些是咱們須要去細細揣度的。

我無從驗證個人理解是否正確,可是個人這個理由已經足夠說服我本身,再往下已再也不是LiveData的範疇,關於這一點我將會專門起一篇文章去進行更深刻的探討,歡迎關注。

--------------------------廣告分割線------------------------------

系列文章

爭取打造 Android Jetpack 講解的最好的博客系列

Android Jetpack 實戰篇


關於我

Hello,我是卻把清梅嗅,若是您以爲文章對您有價值,歡迎 ❤️,也歡迎關注個人我的博客或者Github

若是您以爲文章還差了那麼點東西,也請經過關注督促我寫出更好的文章——萬一哪天我進步了呢?

相關文章
相關標籤/搜索