Android之LiveData

LiveData本身很簡單,但其代表卻正是 MVVM模式最重要的思想,即 數據驅動視圖(也有叫觀察者模式、響應式等)——這也是擺脫 順序性編程思維的重要一步。

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

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

2017年的 Android技術領域,RxJava無疑是炙手可熱的名詞之一,其 觀察者模式和 鏈式調用所表現出來的 API 優秀地設計,使得它位於很多 Android項目技術選型中的 第一序列

這時 Google 隆重推出了具有類似功能的 LiveData(其本質就是觀察者模式),可以說是有點初生牛犢不怕虎的感覺,開發者們不由自主將LiveData和 RxJava進行了對比,結論基本出奇的一致—— LiveData所提供的功能,RxJava完全足以勝任,而後者卻同時具有龐大的生態圈,這是LiveData短時間內難以撼動(替代)的。

時至今日,LiveData的使用者越來越多,最主要的原因當然和Google的強力支持不無關係,但是LiveData本身優秀的設計和輕量級也吸引了越來越多開發者的青睞。

現在我們需要去了解它了,我們都知道,LiveData本質是 觀察者模式的體現,可關鍵的問題是:

觀察者模式到底是啥?!

討論這個問題之前,我們先看看 LiveData的用法,這實在沒什麼技術難度,比如,你可以這樣實例化一個LiveData並使用它:

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

 

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

這似乎和我們日常用到的 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

公佈答案:

 

意外的是,livedata.observer()的本次觀察並沒有觀察到 onCreateonStop和 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…

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

 

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

當然不是,對於這種行爲,我的理解是Google對於LiveData本身嚴格的約束——它只應該用於進行數據的觀察,而不是花哨的操作;轉換爲Flowable當然非常簡單,但是這種行爲是否屬於LiveData本身職責的逾越,更準確來說,是否屬於不必要的過度設計?這些是我們需要去細細揣度的。

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


作者:卻把清梅嗅
鏈接:https://juejin.im/post/5c25753af265da61561f5335

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章