前言
結束了Lifecycle篇和ViewModel篇,終於到了相對複雜的LiveData篇了。
最開始瞭解LiveData我是拒絕的,因為你不能上來就讓我用,馬上就用。第一我要試一下,我不原意用完以後...duang、duang都是bug....
後來用完之後,好嗨呦,感覺人生已經達到了高潮...
正文
當然不想聽我瞎bb的,可以直接官方文件。如果想圖個樂,順便了解了解新技術。那歡迎光臨紅...,男賓一位,裡邊請!
一、概況
官網:LiveData是一個可觀察的資料持有者類。與常規observable不同,LiveData是生命週期感知的。
從官方文件上我們可以看到倆個關鍵詞:可觀察、生命週期感知。簡單來說,Google給我們提供了一個可以被觀察的,並且擁有生命週期感知能力的類。那有什麼用呢?
直接上demo:
二、入門
1.1、初級官方demo
class NameViewModel : ViewModel() {
// 這裡new了一個MutableLiveData,它是LiveData的實現類,LiveData是抽象的,很明顯不能被new
val currentName: LiveData<String> by lazy {
MutableLiveData<String>()
}
}
class NameActivity : AppCompatActivity() {
private lateinit var mModel: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mModel = ViewModelProviders.of(this).get(NameViewModel::class.java)
// 這個this是LifecycleOwner
mModel.currentName.observe(this, Observer { newName ->
// mNameTextView一個TextView
mNameTextView.text = newName
})
// 更新被觀察者資料,LiveData會通知觀察者
mModel.currentName.postValue("MDove")
}
}
複製程式碼
如果看到這幾行程式碼,豁然開朗,那麼可以跳過看下面的部分。如果感覺有些疑惑,不著急我們們不打針不吃藥,坐下就是和你嘮...
1.2、demo解釋
最開始我們先宣告瞭一個NameViewModel的ViewModel,這部分內容在ViewModel篇有所提及。內部有一個MutableLiveData的成員變數。說白了就是一個LiveData型別的String,我們使用時是藉助LiveData的特性,但本質還是用String。
也就是說這裡如果我們要用一個List,那麼此時就是MutableLiveData<List<String>>()
。
Activity之中,我們先獲取ViewModel,然後mModel.currentName.observe(...,...)
,這裡我們就是在觀察LiveData。我們只需要在回撥中處理我們自己的UI操作即可了。也就是demo中的mNameTextView.text = newName
。
LiveData會在每一次postValue(...)
或者value=...
時,observe()
便會回撥,哪怕是null。
注意
這裡有倆個點需要特別注意:
- 1、LiveData是生命週期感知的,在當前的LifecycleOwner不處於活動狀態(例如
onPasue()
、onStop()
)時,LiveData是不會回撥observe()
的,因為沒有意義。 - 2、如果LiveData沒有被
observe()
,那麼此時你呼叫這個LiveData的postValue(...)/value=...,是沒有任何作用。這個我們可以在原始碼中看到。
1.3、不同的LiveData實現類(系統實現)
MutableLiveData:
上文我們們已經見過了,沒什麼特別的,就是LiveData的實現類。就相當於List和ArrayList的關係。
MediatorLiveData:
MutableLiveData的子類,它是一個比較強大的LiveData,我們的map()
、switchMap()
都是基於它進行實現的。
最大的特點是可以同時監聽多個LiveData。
三、進階
官網的這個小破demo,屬實太寒酸了,你倒是加點特技啊?就這中初級用法,誰能覺得好用呢!所以,如果對LiveData稍稍有點感覺,那我們們不要停,一起決戰到天明。
3.1、map()
初用過RxJava的小夥伴,估計會和我一樣,被各種“姿勢”的操作符所正經,比如常用的:map、flatMap...而LiveData中同樣有這樣的操作。
一個很常見的場景:我們通過一個唯一id去查詢這個id下的實體類,並且要同時展示二者的資料。很簡單的業務邏輯,在LiveData中的展示是這樣的:
3.1.1、使用
class NameViewModel : ViewModel() {
val userIdLiveData = MutableLiveData<Long>()
// 偽碼:當userIdLiveData發生變化時,userLiveData中的map就會呼叫,那麼我們就可以得到罪行的id
val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
// 通過id拿到User,return一個User的例項user
user
}
}
// Activity中
mModel.userLiveData.observe(this, Observer { user ->
// user變化後通知mNameTextView更新UI
mNameTextView.text = user.name
})
// 給userIdLiveData設定id為1
mModel.userIdLiveData.postValue("1")
複製程式碼
針對這個業務場景,我們只需要監聽我們使用者通知UI變化的LiveData(userLiveData),然後通過userIdLiveData.postValue("1")
來驅動資料的變化。
這裡可能和我們傳統的MVP的思想並不相同,畢竟MVVM和MVP還是有區別的,而MVVM的這種方式被稱之為:資料驅動。
3.1.2、map()原始碼
我們直接點到Transformations.map()
中。
@MainThread
public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
複製程式碼
很簡單,就是使用了MediatorLiveData
,然後通過一個高階函式,將高階函式返回的內容,set到LiveData上,完成map()。
既然提到了MediatorLiveData
,以及它的addSource()
的方法,那麼我們就來看看它的原始碼。
3.1.3、MediatorLiveData原始碼
這部分沒啥意思,可以直接跳過看3.1.4、map()原始碼總結...
進入MediatorLiveData
之中,我們會發現程式碼比較少。這裡抽出倆塊比較重點的內容,我們一同來感受一下:
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
複製程式碼
從這段程式碼中,我們粗略可以得到一個資訊,這裡把我們的LiveData和Observer封裝成了Source物件,並且這個物件,不能重複新增。
此外,Source的plug()方法,被呼叫。接下來我們去看一看這個內部類Source的實現:
private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
mLiveData.observeForever(this);
}
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
複製程式碼
首先Source是一個觀察者,可以看到,我們外部使用的Observer會以Source的成員變數的形式,新增到傳入的LiveData中。值得注意的是,這裡使用了mLiveData.observeForever(this);
。
從observeForever()
用法可以看到,我們並沒有傳遞LifecycleOwner
,因此它並不具備生命感知能力。從註釋中也可見一斑:This means that the given observer will receive all events and will never be automatically removed.
3.1.4、map()原始碼總結
打住,打住吧。其實沒必要繼續看了。一句話總結:map()的原理就是基於MediatorLiveData,MediatorLiveData內部會將傳遞進來的LiveData和Observer封裝成內部類,然後放在內部維護的一個Map中。並且自動幫我們完成observeForever
()和removeObserver()
。
3.2、switchMap()
3.2.1、使用
switchMap()的場景可以應用在切換LiveData上。這話怎麼解釋呢?
很常見的業務場景:比如你的業務用的是map(),map()中使用你自己寫的絡,而且LiveData執行的很良好,抽著煙喝著酒,啥事都沒有...就比如,上面map()那樣的程式碼:
val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
// 自己的一段邏輯
user
}
// Activity中
mViewModel.userLiveData.observe(this,Observer{->user
//更新UI
})
複製程式碼
突然有一天,這個地方資料結構、UI都沒變,唯獨變了邏輯。此時你一個同事寫好了一個方法,讓你替換一下就好了了。不過當你呼叫的時候突然返現,這個方法返回一個LiveData物件!
當然此時我們可以讓UI重新observe()這個LiveData物件:
val otherLiveData:LiveData<User> = // 同事的邏輯
// Activity中重新observe()
mViewModel.otherLiveData.observe(this,Observer{->user
//更新UI
})
複製程式碼
可是這樣的話,自己之前寫的東西不都白費了麼?所以此時,我們可以使用switchMap()
,我們只需要很少的改動,就可以設配這次需求的變動:
val userLiveData: LiveData<User> = Transformations.switchMap(userIdLiveData) { id->
// 直接把同事的程式碼放在這裡即可
}
複製程式碼
3.2.2、switchMap()原始碼
有了上邊map()
原始碼基礎,我們可以很容易的看出switchMap()
的端倪:
@MainThread
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 從Function中拿到返回的LiveData,也就是我們新的LiveData(文章背景中同事寫的LiveData)
LiveData<Y> newLiveData = switchMapFunction.apply(x);
if (mSource == newLiveData) {
return;
}
// remove掉舊的LiveData
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
// add新的LiveData
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
// 通知LiveData發生變化
result.setValue(y);
}
});
}
}
});
return result;
}
複製程式碼
我們對比一下switchMap()
和map()
的引數型別:
- Function<X, LiveData> switchMapFunction
- Function<X, Y> mapFunction
很明顯一個是返回LiveData型別,一個是換一種類型。這也說明了,這倆個方法的不一樣之處 。
程式碼解釋可以看註釋,很直白的思路。
3.3、MediatorLiveData的使用
上述我們看過了map()
和switchMap()
的用法。各位應該都注意到MediatorLiveData
這個類的作用。
上邊我們一直都在操作一個LiveData,但是我們需求很容易遇到多種狀態的變化。就像官方的demo:
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
複製程式碼
如同demo所示,我們可以同時add多個LiveData,根據不同的LiveData的變化,處理我們不同的邏輯。最後通過MediatorLiveData回撥到我們的UI上。
四、原始碼分析
註冊Observer
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 如果當前生命週期是DESTROYED,直接return
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
// 這個包裝類,做了一件事情,在DESTROYED,移除Observer
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 新增在已經Observer,已存在且在Attach上後直接拋異常,也就是不能重複add
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 把Wrapper新增到LifecycleOwner上
owner.getLifecycle().addObserver(wrapper);
}
複製程式碼
Observer如何被響應:
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}
複製程式碼
觸發的起點,很明顯是我們在set/postValue的時候:
@MainThread
protected void setValue(T value) {
// 記住這個值,它是用來表示資料是否發生變化的
mVersion++;
mData = value;
dispatchingValue(null);
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// 省略部分程式碼
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// 往裡走
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
// 省略部分程式碼
}
private void considerNotify(ObserverWrapper observer) {
// 如果observer不在活動期,則直接return。也就是上述說observer不在前臺,將不會接受回撥。
if (!observer.mActive) {
return;
}
// 省略部分程式碼
// 很直白的version對比
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 回撥
observer.mObserver.onChanged((T) mData);
}
複製程式碼
observer.mActive
在哪被賦值?很多地方。除了一些邊界條件的賦值外,一個比較正式的賦值,ObserverWrapper
中的void activeStateChanged(boolean newActive)
方法:
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
mActive = newActive;
}
// 此方法最終會調到此方法
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
複製程式碼
很簡單,每次生命週期回撥,observer.mActive
都會被賦值,而只有到Lifecycle是活動狀態是,mActive才會true。因此只有在我們的Activity為前臺時我們的LiveData
才會被回撥。
尾聲
到此關於LiveData的部分就整完了,不知道各位看官是否感受到LiveData的好用之處了呢?如果沒有,不如自己寫一寫,用身體去感受來自LiveData的爽快~