Livedata 概覽
LiveData 是一種可觀察的資料儲存器類。與常規的可觀察類不同,LiveData 具有生命週期感知能力
如果觀察者(由 Observer 類表示)的生命週期處於 STARTED 或 RESUMED 狀態,則 LiveData 會認為該觀察者處於活躍狀態。。LiveData 只會將更新通知給活躍的觀察者。為觀察 LiveData 物件而註冊的非活躍觀察者不會收到更改通知。
您可以註冊與實現 LifecycleOwner 介面的物件配對的觀察者。有了這種關係,當相應的 Lifecycle 物件的狀態變為 DESTROYED 時,便可移除此觀察者。這對於 Activity 和 Fragment 特別有用,因為它們可以放心地觀察 LiveData 物件,而不必擔心洩露
LiveData 優勢
資料符合頁面狀態
不會發生記憶體洩露
不會因 activity 停止而導致崩潰
不再需要手動處理生命週期
資料始終保持最新狀態
可以用來做資源共享
Livedata 使用
一般來說我們會在 ViewModel 中建立 Livedata 物件,然後再 Activity/Fragment 的 onCreate 中註冊 Livedata 監聽(因為在 onStart 和 onResume 中進行監聽可能會有冗餘呼叫)
Livedata 簡單使用
仍然還是用我們倒計時的例子,在 Viewmodel 中開始一個 2000s 的倒計時,然後透過 Livedata 回撥給 Activity 進行更新介面,程式碼:
viewmodel 程式碼
class CountDownModel : ViewModel() { val countDownLivedata = MutableLiveData
activity 中觀察資料更新 ui 程式碼
val countDownModel: CountDownModel by viewModels
效果圖
使用全域性 Livedata 在多個檢視監聽狀態
本例實現的 demo 效果是,建立一個全域性的倒計時,然後在 Activity 中新增兩個按鈕,點選後可以切換 FragmentA 和 FragmentB。然後我們透過全域性的自定義 LiveData 單例實現資料監聽,切換 Fragment 後 Fragment 頁面上會展示倒計時的剩餘秒數
程式碼:
全域性自定義 Livedata 程式碼
class GlobalLivedata : LiveData
倒計時器程式碼較長只貼上一部分,有興趣可以到 github 去檢視完整程式碼
private val listeners = mutableListOf
FragmentA、FragmentB 中監聽倒計時狀態
GlobalLivedata。getInstance()。observe(viewLifecycleOwner, { t -> inflate。findViewById
GlobalLivedata。getInstance()。observe(viewLifecycleOwner, { t -> inflate。findViewById
最終效果
最終效果,當我們切換 Fragment 的時候兩個 Fragment 顯示的秒數是一致的,其實即使我們馬上啟動一個新 activity 去檢視剩餘秒數也是一樣的,有興趣的朋友可以下載 git 程式碼自己嘗試
對 Livedata 進行轉換
map 和 switchMap 兩個方法可以對已有的 Livedata 進行轉換得到新的 Livedata
Transformation。map
在 activity 中觀察 viewmodel 中的資料更新,當點選 activity 中按鈕的時候會呼叫 viewmodel。sendData 方法傳送資料,然後傳送的資料會做一定的轉換給 activity,然後 activity 列印日誌展示
直接看程式碼吧:
建立 viewmodel,model 中建立 Livedata
class TransMapViewModel: ViewModel() { fun sendData() { userLivedata。value=User(“李白”,1200)//對userLivedata進行復制 } val userLivedata =MutableLiveData
程式碼中 mapLiveData 是對 userLivedata 進行轉換得到的,所以當我們呼叫 sendData 方法更新 userLivedata 中的方法時,mapLiveData 的回撥也會觸發
在 activity 中觀察 mapLiveData 並點選按鈕傳送小資料
mapViewModel。mapLiveData。observe(this,{ logEE(it) tv_map。text=it }) btn_map。setOnClickListener { mapViewModel。sendData() }複製程式碼
Transformation。switchMap
本例中我們實現如下邏輯:
在 activity 中觀察 viewmodel 中的資料更新,當點選 activity 中按鈕的時候會呼叫 viewmodel。sendData 方法傳送資料,然後傳送的資料會做一定的轉換給 activity,然後 activity 列印日誌展示
viewmodel 中程式碼
class SwitchMapViewModel : ViewModel() { fun sendData() { userLivedata。value = SwitchUser(“李白”, 1200) } private val userLivedata = MutableLiveData
呼叫部分程式碼
model。mapLiveData。observe(this, { logEE(it) }) btn_switchmap。setOnClickListener { model。sendData() }複製程式碼
合併兩個 Livedata(MediatorLiveData)
想象這樣一個場景,您的 app 裡面有一個評論列表的功能,可以對列表內容進行點贊。每一個點贊都是一個非同步任誤,你的產品需求並不想讓使用者點太多贊,比如一分鐘點贊數量不能超過 10 次,這種場景就很適合用 Livedata 的合併功能
我們就不模擬這麼複雜的場景了,我們的例子做這樣一個事情:
介面上有兩個按鈕,點一次相當於點贊一次,我們點選十次按鈕就在介面上展示文字提示使用者已經點選了十次資料。
程式碼展示:
1。model 程式碼
class MeditorLiveViewModel : ViewModel() { var count =0//計數字段 fun setData1(name: String) { liveData1。value = name } fun setData2(age: Int) { liveData2。value = age } private val liveData1 = MutableLiveData
model 中建立了三個 Livedata,其中兩個分別是 livedata1 和 livedata2,分別對應其中兩個按鈕。
還有一個 liveCombind 用來回調超過十次呼叫的場景
init 方法中 liveCombind。addSource 呼叫就是表示用來中間攔截 livedata1 和 livedata2 的資料更新,處理 count 累加和是否回撥 liveCombind 的功能
activity 中程式碼
model。liveCombind。observe(this){ logEE(it) tv_count。text=it } btn_livedata1。setOnClickListener { model。setData1(“李白”) } btn_livedata2。setOnClickListener { model。setData2(1000) }複製程式碼
實現效果
observeForever
observeForever 方法也是註冊 Livedata 監聽的方法,表示即使應頁面被覆蓋處於不活躍狀態也可以收到資料改變的回撥
Livedata 和協程聯合使用
emit 方式使用
引入依賴 有時候你可能需要處理非同步任務,任務處理完成後重新整理 ui
這種情況可以使用 Livedata 的擴充套件程式實現
本例我們實現下面的邏輯:
在 viewmodel 中阻塞 4s,然後通知 activity
程式碼:
引入依賴外掛
implementation ‘androidx。lifecycle:lifecycle-livedata-ktx:2。2。0’複製程式碼
開啟非同步任務方法
/** * 開啟非同步任務 */ fun startAsyncWithSecond(second: Int): LiveData
當我們呼叫 startAsyncWithSecond 方法的時候會馬上返回一個 Livedata 物件,供我們註冊監聽
activity 中註冊 livedata 監聽
model。startAsyncWithSecond(3)。observe(this){ logEE(it)//model中delay 3s後會返回資料到這裡 }複製程式碼
效果展示
emitSource 使用
使用 emitSource 的效果等同於 MediatorLiveData 的效果
我們本例實現如下的效果:
點選按鈕開啟一個 3s 的非同步任務,然後通知 activity 列印日誌。
然後再次開啟一個 3s 的非同步任務,結束後再次通知 activity 列印日誌
程式碼:
建立非同步任務方法
fun startAsyncEmitSource(second: Int)= liveData
activity 中註冊監聽
model。startAsyncEmitSource(3)。observe(this){ logEE(it) }複製程式碼
效果
本文專案原始碼可以在我的Github自取