本文翻譯自【Understanding LiveData made simple】,詳細介紹了 liveData 的使用。感謝做者 Elye。水平有限,歡迎指正討論。 Architecture Components 能夠說是 Google 提供給 Android 開發者的一大福利。LiveData 是其中的一個主要組件,下面咱們一塊兒看下該怎麼使用好 LiveData
。 若是你以前沒了解過 Architecture Components
,能夠看下做者的另外一篇文章:Android Architecture Components for Dummies in Kotlin (50 lines of code)。 在上一篇文章中,做者提到了 ViewModel
和 LiveData
,其中 LiveData 是用來從 ViewModel 層向 View 層傳遞數據。但當時並無完整地介紹 LiveData 的做用,如今咱們來詳細看下 LiveData 的定義和使用。 那麼,LiveData 有什麼特別的地方呢?android
官方 定義是:git
LiveData 是一個可被觀察的數據持有類。與普通的被觀察者(如 RxJava 中的 Observable)不一樣的是,LiveData 是生命週期感知的,也就是說,它能感知其它應用組件(Activity,Fragment,Service)的生命週期。這種感知能力能夠確保只有處於 active 狀態的組件才能收到 LiveData 的更新。詳情可查看 Lifecycle。github
這就是官方對 LiveData 的定義。 爲了簡單起見,咱們先來看一些以前的開發習慣,以便更好地理解。數據庫
當 Android 剛誕生的時候,大多數開發者寫的代碼都放在一個 Activity 中。 bash
然而,把全部邏輯都放在一個 Activity 類中並不理想。由於 Activity 很難進行單元測試。 鑑於此,業界出現了MVC、MVP、MVVM 等開發架構,經過 Controller、Presenter、ViewModel 等分層抽離 Activity 中的代碼。 網絡
這種架構能把邏輯從 View 層分離出來。然而,它的問題是 Controller、Presenter、ViewModel 等不能感知 Activity 的生命週期,Activity 的生命週期必須通知這些組件。 爲了統一解決方案,Google 開始重視這個問題,因而 Architecture Components 誕生了。 其中 ViewModel 組件有一個特殊能力,咱們不須要手動通知它們,就能夠感知 Activity 的生命週期。這是系統內部幫咱們作的事情。 session
除了 ViewModel 外,用於從 ViewModel 層暴露到 View 層的數據,也有生命週期感知的能力,這就是爲何叫作 LiveData
的緣由。做爲一個被觀察者,它能夠感知觀察它的 Activity 的生命週期。架構
爲了更好地理解,下圖將 LiveData 做爲數據中心: app
從上圖能夠看到,LiveData 的數據來源通常是 ViewModel,或者其它用來更新 LiveData 的組件。一旦數據更新後,LiveData 就會通知它的全部觀察者,例如 Activity、Fragment、Service 等組件。可是,與其餘相似 RxJava 的方法不一樣的是,LiveData 並非盲目的通知全部觀察者,而是首先檢查它們的實時狀態。LiveData 只會通知處於 Actie 的觀察者,若是一個觀察者處於 Paused 或 Destroyed 狀態,它將不會受到通知。 這樣的好處是,咱們不須要在 onPause
或 onDestroy
方法中解除對 LiveData 的訂閱/觀察。此外,一旦觀察者從新恢復 Resumed 狀態,它將會從新收到 LiveData 的最新數據。ide
LiveData
是一個抽象類,咱們不能直接使用。幸運的是,Google 提供了一些其簡單實現,讓咱們來使用。
MutableLiveData 是 LiveData 的一個最簡單實現,它能夠接收數據更新並通知觀察者。 例如:
// 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
數據發生變化(例如7567和6269),Fragment 就會收到更新。
上面的代碼中有這麼一行:
getLiveDataA().observe(this, changeObserver)
複製代碼
這就是訂閱 LiveData 的地方,可是並無在 Fragment pausing 或 terminating 時解除訂閱。 即便咱們沒有解除訂閱,也不會有什麼問題。看下面的例子,當 Fragment 銷燬時,LiveData 不會由於產生一個新數據(1428)通知給 inactive 的 Fragment 而崩潰(Crash)。
同時,也能夠看到當 Fragment 從新 active 時,將會收到最新的 LiveData 數據:1428。
咱們通常定義一個 Repository 負責從網絡或數據庫獲取數據,在將這些數據傳遞到 View 層以前,可能須要作一些處理。 以下圖,咱們使用 LiveData 在各個層之間傳遞數據:
咱們能夠使用 Transformations#map() 方法將數據從一個 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
的數據(5116)進行處理後變爲 A:5116。
使用 Transformations#map()
有助於確保 LiveData 的數據不會傳遞給處於 dead 狀態的 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
。接下來看一看這是個什麼東西。
從 Transformations#map()
源碼中能夠看到,MediatorLiveData 有一個 MediatorLiveData#addSource() 方法,這個方法改變了數據內容。 也就是說,咱們能夠經過 MediatorLiveData
將多個 LiveData 源數據集合起來,以下圖所示:
代碼以下:
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 ..
}
複製代碼
這樣,Fragment 就能夠同時接收到 LiveDataA
和 LiveDataB
的數據變化,以下圖所示:
有一點須要注意的是:當 Fragment 不 再處於 active
狀態時,若是 LiveDataA
和 LiveDataB
的數據都發生了變化,那麼當 Fragment 從新恢復 active 狀態時,MediatorLiveData
將獲取最後添加的 LiveData 的數據發送給 Fragment,這裏即 LiveDataB
。
從上圖能夠看到,當 Fragment 恢復活動狀態時,它就會收到 LiveDataB
的最新數據,不管 LiveDataB
變化的比 LiveDataA
變化的早或晚。從上面代碼能夠看到,這是由於 LiveDataB
是最後被添加到 MediatorLiveData
中的。
上面的示例中展現了咱們能夠同時監聽兩個 LiveData 的數據變化,這是頗有用的。可是,若是咱們想要手動控制只監聽其中一個的數據變化,並能根據須要隨時切換,這時應怎麼辦呢? 答案是: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;
}
複製代碼
這個方法用來添加一個新數據源並相應地刪除前一個數據源。所以 MediatorLiveData
只會包含一個 LiveData 數據源。這個控制開關也是一個 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 ..
}
複製代碼
這樣,咱們就能很容易地控制用哪一個數據來更新 View 視圖,以下所示,當正在觀察的 LiveData 發生變化,或者切換觀察的 LiveData 時,Fragment 都會收到數據更新。
一個實際的使用場景是,咱們能夠經過特定設置(如用戶登陸 session)的不一樣數據源,來處理不一樣的業務邏輯。
以上示例代碼能夠在做者的 Github 上找到:github.com/elye/demo_a…。 下載源碼查看,能更好地理解。
若是訂閱了一個 LiveData,但又不想收到數據更新的通知,能夠參考一下文章: LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)。
我是 xiaobailong24,您能夠經過如下平臺找到我: