前言
本文翻譯自【Understanding LiveData made simple】,詳細介紹了 liveData 的使用措嵌。感謝作者 Elye。水平有限芦缰,歡迎指正討論企巢。
Architecture Components 可以說是 Google 提供給 Android 開發(fā)者的一大福利。LiveData 是其中的一個主要組件饺藤,下面我們一起看下該怎么使用好 LiveData
包斑。
如果你之前沒了解過 Architecture Components
,可以看下作者的另一篇文章:Android Architecture Components for Dummies in Kotlin (50 lines of code)涕俗。
在上一篇文章中,作者提到了 ViewModel
和 LiveData
神帅,其中 LiveData 是用來從 ViewModel 層向 View 層傳遞數(shù)據再姑。但當時并沒有完整地介紹 LiveData 的作用,現(xiàn)在我們來詳細看下 LiveData 的定義和使用找御。
那么元镀,LiveData 有什么特別的地方呢?
正文
什么是 LiveData
官方 定義是:
LiveData 是一個可被觀察的數(shù)據持有類霎桅。與普通的被觀察者(如 RxJava 中的 Observable)不同的是栖疑,LiveData 是生命周期感知的,也就是說滔驶,它能感知其它應用組件(Activity遇革,F(xiàn)ragment,Service)的生命周期。這種感知能力可以確保只有處于 active 狀態(tài)的組件才能收到 LiveData 的更新萝快。詳情可查看 Lifecycle锻霎。
這就是官方對 LiveData 的定義。
為了簡單起見揪漩,我們先來看一些之前的開發(fā)習慣旋恼,以便更好地理解。
起源
當 Android 剛誕生的時候奄容,大多數(shù)開發(fā)者寫的代碼都放在一個 Activity 中冰更。
然而,把所有邏輯都放在一個 Activity 類中并不理想昂勒。因為 Activity 很難進行單元測試蜀细。
鑒于此,業(yè)界出現(xiàn)了MVC叁怪、MVP审葬、MVVM 等開發(fā)架構,通過 Controller奕谭、Presenter涣觉、ViewModel 等分層抽離 Activity 中的代碼。
這種架構能把邏輯從 View 層分離出來血柳。然而官册,它的問題是 Controller、Presenter难捌、ViewModel 等不能感知 Activity 的生命周期膝宁,Activity 的生命周期必須通知這些組件。
為了統(tǒng)一解決方案根吁,Google 開始重視這個問題员淫,于是 Architecture Components 誕生了。
其中 ViewModel 組件有一個特殊能力击敌,我們不需要手動通知它們介返,就可以感知 Activity 的生命周期。這是系統(tǒng)內部幫我們做的事情沃斤。
除了 ViewModel 外圣蝎,用于從 ViewModel 層暴露到 View 層的數(shù)據,也有生命周期感知的能力衡瓶,這就是為什么叫做 LiveData
的原因徘公。作為一個被觀察者,它可以感知觀察它的 Activity 的生命周期哮针。
舉例說明
為了更好地理解关面,下圖將 LiveData 作為數(shù)據中心:
從上圖可以看到坦袍,LiveData 的數(shù)據來源一般是 ViewModel,或者其它用來更新 LiveData 的組件缭裆。一旦數(shù)據更新后键闺,LiveData 就會通知它的所有觀察者,例如 Activity澈驼、Fragment辛燥、Service 等組件。但是缝其,與其他類似 RxJava 的方法不同的是挎塌,LiveData 并不是盲目的通知所有觀察者,而是首先檢查它們的實時狀態(tài)内边。LiveData 只會通知處于 Actie 的觀察者榴都,如果一個觀察者處于 Paused 或 Destroyed 狀態(tài),它將不會受到通知漠其。
這樣的好處是嘴高,我們不需要在 onPause
或 onDestroy
方法中解除對 LiveData 的訂閱/觀察。此外和屎,一旦觀察者重新恢復 Resumed 狀態(tài)拴驮,它將會重新收到 LiveData 的最新數(shù)據。
LiveData 的子類
LiveData
是一個抽象類柴信,我們不能直接使用套啤。幸運的是,Google 提供了一些其簡單實現(xiàn)随常,讓我們來使用潜沦。
MutableLiveData
MutableLiveData 是 LiveData 的一個最簡單實現(xiàn),它可以接收數(shù)據更新并通知觀察者绪氛。
例如:
// Declaring it
val liveDataA = MutableLiveData<String>()
// Trigger the value change
liveDataA.value = someValue
// Optionally, one could use liveDataA.postValue(value)
// to get it set on the UI thread
觀察 LiveData 也很簡單唆鸡,下面展示了在 Fragment 中訂閱 LiveDataA
:
class MutableLiveDataFragment : Fragment() {
private val changeObserver = Observer<String> { value ->
value?.let { txt_fragment.text = it }
}
override fun onAttach(context: Context?) {
super.onAttach(context)
getLiveDataA().observe(this, changeObserver)
}
// .. some other Fragment specific code ..
}
結果如下,一旦 LiveDataA
數(shù)據發(fā)生變化(例如7567和6269)枣察,F(xiàn)ragment 就會收到更新喇闸。
上面的代碼中有這么一行:
getLiveDataA().observe(this, changeObserver)
這就是訂閱 LiveData 的地方,但是并沒有在 Fragment pausing 或 terminating 時解除訂閱询件。
即使我們沒有解除訂閱,也不會有什么問題唆樊⊥鹄牛看下面的例子,當 Fragment 銷毀時逗旁,LiveData 不會因為產生一個新數(shù)據(1428)通知給 inactive 的 Fragment 而崩潰(Crash)嘿辟。
同時舆瘪,也可以看到當 Fragment 重新 active 時,將會收到最新的 LiveData 數(shù)據:1428红伦。
Transformations#map()
我們一般定義一個 Repository 負責從網絡或數(shù)據庫獲取數(shù)據英古,在將這些數(shù)據傳遞到 View 層之前,可能需要做一些處理昙读。
如下圖召调,我們使用 LiveData 在各個層之間傳遞數(shù)據:
我們可以使用 Transformations#map() 方法將數(shù)據從一個 LiveData 傳遞到另一個 LiveData。
class TransformationMapFragment : Fragment() {
private val changeObserver = Observer<String> { value ->
value?.let { txt_fragment.text = it }
}
override fun onAttach(context: Context?) {
super.onAttach(context)
val transformedLiveData = Transformations.map(
getLiveDataA()) { "A:$it" }
transformedLiveData.observe(this, changeObserver)
}
// .. some other Fragment specific code ..
}
結果如下所示蛮浑,以上代碼將 LiveDataA
的數(shù)據(5116)進行處理后變?yōu)?A:5116唠叛。
使用 Transformations#map()
有助于確保 LiveData 的數(shù)據不會傳遞給處于 dead 狀態(tài)的 ViewModel 和 View。
這很酷沮稚,我們不用擔心解除訂閱艺沼。
下面來看下 Transformations#map()
的源碼:
@MainThread
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
@NonNull final Function<X, Y> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(func.apply(x));
}
});
return result;
}
這里用到了 LiveData 的另一個子類 MediatorLiveData
。接下來看一看這是個什么東西蕴掏。
MediatorLiveData
從 Transformations#map()
源碼中可以看到障般,MediatorLiveData 有一個 MediatorLiveData#addSource() 方法,這個方法改變了數(shù)據內容盛杰。
也就是說挽荡,我們可以通過 MediatorLiveData
將多個 LiveData 源數(shù)據集合起來,如下圖所示:
代碼如下:
class MediatorLiveDataFragment : Fragment() {
private val changeObserver = Observer<String> { value ->
value?.let { txt_fragment.text = it }
}
override fun onAttach(context: Context?) {
super.onAttach(context)
val mediatorLiveData = MediatorLiveData<String>()
mediatorLiveData.addSource(getliveDataA())
{ mediatorLiveData.value = "A:$it" }
mediatorLiveData.addSource(getliveDataB())
{ mediatorLiveData.value = "B:$it" }
mediatorLiveData.observe(this, changeObserver)
}
// .. some other Fragment specific code ..
}
這樣饶唤,F(xiàn)ragment 就可以同時接收到 LiveDataA
和 LiveDataB
的數(shù)據變化,如下圖所示:
有一點需要注意的是:當 Fragment 不 再處于 active
狀態(tài)時徐伐,如果 LiveDataA
和 LiveDataB
的數(shù)據都發(fā)生了變化,那么當 Fragment 重新恢復 active 狀態(tài)時募狂,MediatorLiveData
將獲取最后添加的 LiveData 的數(shù)據發(fā)送給 Fragment办素,這里即 LiveDataB
。
從上圖可以看到祸穷,當 Fragment 恢復活動狀態(tài)時性穿,它就會收到 LiveDataB
的最新數(shù)據,無論 LiveDataB
變化的比 LiveDataA
變化的早或晚雷滚。從上面代碼可以看到需曾,這是因為 LiveDataB
是最后被添加到 MediatorLiveData
中的。
Transformations#switchMap
上面的示例中展示了我們可以同時監(jiān)聽兩個 LiveData 的數(shù)據變化祈远,這是很有用的呆万。但是,如果我們想要手動控制只監(jiān)聽其中一個的數(shù)據變化车份,并能根據需要隨時切換谋减,這時應怎么辦呢?
答案是:Transformations#switchMap()扫沼,Google 已經為我們提供了這個方法出爹。它的定義如下:
@MainThread
public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
@NonNull final Function<X, LiveData<Y>> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(trigger, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
LiveData<Y> newLiveData = func.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
這個方法用來添加一個新數(shù)據源并相應地刪除前一個數(shù)據源庄吼。因此 MediatorLiveData
只會包含一個 LiveData 數(shù)據源。這個控制開關也是一個 LiveData严就。整個過程如下所示:
使用方法如下:
class TransformationSwitchMapFragment : Fragment() {
private val changeObserver = Observer<String> { value ->
value?.let { txt_fragment.text = it }
}
override fun onAttach(context: Context?) {
super.onAttach(context)
val transformSwitchedLiveData =
Transformations.switchMap(getLiveDataSwitch()) {
switchToB ->
if (switchToB) {
getLiveDataB()
} else {
getLiveDataA()
}
}
transformSwitchedLiveData.observe(this, changeObserver)
}
// .. some other Fragment specific code ..
}
這樣总寻,我們就能很容易地控制用哪個數(shù)據來更新 View 視圖,如下所示梢为,當正在觀察的 LiveData 發(fā)生變化渐行,或者切換觀察的 LiveData 時,F(xiàn)ragment 都會收到數(shù)據更新抖誉。
一個實際的使用場景是殊轴,我們可以通過特定設置(如用戶登錄 session)的不同數(shù)據源,來處理不同的業(yè)務邏輯袒炉。
源碼地址
以上示例代碼可以在作者的 Github 上找到:https://github.com/elye/demo_android_livedata_illustration旁理。
下載源碼查看,能更好地理解我磁。
更多示例
如果訂閱了一個 LiveData孽文,但又不想收到數(shù)據更新的通知,可以參考一下文章:
LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)夺艰。
參考
- Understanding LiveData made simple
- LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)
- LiveData
- MediatorLiveData
- MutableLiveData
- Transformations
聯(lián)系
我是 xiaobailong24芋哭,您可以通過以下平臺找到我: