LiveData
1,LiveData簡介
LiveData是Jetpack庫集合中的一員,從名字看來,它是一種數據。更準確的說,它是一種數據容器。它提供了一些功能,使得這種數據不僅僅是簡單的數據,而是一種可觀測的、可感知組件生命週期的數據。
public abstract class LiveData<T> {...}
LiveData
是一種可觀測的數據,我們可以向LiveData中註冊觀察者Observer
,那麼當LiveData的數據發生改變的時候,就會主動通知觀察者(調用Observer的onChanged方法)。並且,這種通知是可以遵循組件生命週期的,當生命週期狀態至少STARTED
的時候LiveData纔會去通知它,否則的話不去發送。但是,當組件重新恢復活躍狀態的時候,LiveData就會去通知它,然後就能夠拿到最新的數據。
這裏的STARTED的指的是Lifecycle的states,一共有5種狀態,分別是:INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED,不瞭解的可以先去看一下Jetpack的另一個組件Lifecycle
活躍狀態指的是Lifecycle的狀態至少處於STARTED,也就是STARTED和RESUMED,只有這兩種狀態纔會被LiveData認爲是活躍狀態,才能夠接收到LiveData的最新消息。
LiveData會根據與觀察者綁定的組件的生命週期狀態去選擇是否通知觀察者數據發生了更新,因此LiveData要求在註冊觀察的時候,需要傳遞一個LifecycleOwner
參數。LiveData通過這個參數去獲取它的生命週期狀態,然後決定是否要將事件發送給對應的觀察者。而註冊時的另一個參數是Observer,它是一個單方法接口,LiveData通過它的onChanged
方法去通知最新的數據。
public interface LifecycleOwner {
// LiveData根據這個方法去拿到Lifecycle從而獲取生命週期狀態
@NonNull
Lifecycle getLifecycle();
}
public interface Observer<T> {
// 當LiveData的值修改後將會調用這個方法,參數t就是LiveData的值
void onChanged(T t);
}
2,使用LiveData
只需要在build.gradle中聲明以下即可引入livedata的2.2.0版本,後面的源碼都是基於此版本。
// Kotlin項目
implementation ‘androidx.lifecycle:lifecycle-livedata-ktx:2.2.0’
// Java項目
implementation ‘androidx.lifecycle:lifecycle-livedata:2.2.0’
LiveData是一個抽象類,並且它對外修改數據的兩個方法setValue/postValue
都被標記爲protected,因此它是不可修改的,實際中常使用它的子類MutableLiveData
。MutableLiveData相對於LiveData沒有其他大的改變,只是將setValue和postValue標記爲了public
而已。這兩個方法中setValue是用作主線程修改數據的,postValue可以在子線程中修改LiveData的值。
class MainActivity : AppCompatActivity() {
private val stringLiveData = MutableLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
stringLiveData.observe(this, Observer {
textView.text = it
})
btnChange.setOnClickListener {
stringLiveData.value = "value changed"
}
}
}
上面是使用LiveData的一個最簡單的案例,在MainActivity中聲明瞭一個stringLiveData,並且在onCreate中註冊觀察者,觀察事件就是當LiveData的值發生改變的時候,將textView設置爲這個值。當按鈕btnChange點擊的時候,會將LiveData的值修改爲“value changed”,這時LiveData就會通知觀察者,因此就會將textView設置爲value changed。
注意,LiveData是可以感知組件的生命週期的,這就意味着它應該比組件存在的時間更長一點,那麼我們在Activity中去定義LiveData就是一個不恰當的做法,實際中應該在ViewModel
中或者在單例中去定義LiveData。
在上例中就使用MainActivity
與觀察者一起註冊在LiveData
中,因此這個觀察者Observer
能否接收到消息就取決於MainActivity的生命週期了。Fragment中也可以傳入this,但是Fragment中還有一個參數viewLifecycleOwner
,它也是LifecycleOwner
。
viewLifecycleOwner
與Fragment
本身生命週期有一點不同,ViewLifecycleOwner是專注於View的生命週期,它在onCreateView之後纔會被創建,此時處於INITIALIZED狀態。在onActivityCreated後爲CREATED狀態,在onDestroyView之後處於DESTROYED狀態。也就是說**,viewLifecycleOwner的生命週期狀態是在onCreateView到onDestroyView之間**。
而Fragment本身更專注Fragment的生命週期,它在構造方法中初始化處於INITIALIZED,然後在onCreate後處於CREATED,在onDestroy後處於DESTROYED狀態。也就是說使用Fragment本身的生命週期狀態是在構造方法到onDestroy之間。
那麼我們在Fragment註冊觀察者的時候就要注意選擇viewLifecycleOwner
還是this
。我們知道在使用ViewPager
和FragmentPagerAdapter
的時候,默認緩存左右各一個Fragment,當切換的時候,會觸發Fragment的onPause、onStop、onDestroyView
。若是我們此時在Fragment觀察LiveData的時候使用的是this,那麼在ViewPager切換回來的時候在onCreateView
中又會去註冊一次(前面說過在onCreateView之後才能使用viewLifecycleOwner),這樣重複註冊就會在成當LiveData修改後收到多次事件的情景。
總結:Fragment中關乎view的使用viewLifecycleOwner(大部分情況我們都是使用viewLifecycleOwner),不在乎View而專注Fragment的可以使用this(例如我們添加一個不帶界面的Fragment,這種情況也不少,比如Glide就是通過創建一個無界面的Fragment來管理什麼時候去加載圖片什麼時候停止加載,比如還可以通過空的Fragment去申請權限等不需要界面的操作,這時候就應該用this而不是viewLifecycleOwner)。
3,LiveData源碼分析
3.1 ,註冊成爲LiveData的一名觀察者
從前面那個簡單的例子可以看出,要觀察到LiveData的數據變更必須要先註冊成爲LiveData的一名觀察者,這樣才能接收到數據的變化。
// MainActivity
stringLiveData.observe(this, Observer {
textView.text = it
})
//LiveData
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
// 首先拿到生命週期狀態,處於DESTROYED時不去綁定,該狀態是onDestroy後的狀態
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 將LifecycleOwner與Observer綁定在一起
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 從已經註冊集合中查找是否已經註冊過,未註冊的會添加到mObservers中並且返回null
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 若是已經註冊過,並且註冊的owner與當前不一致。這種情況是這樣:定義一個Observer事件,然後在Activity中註冊,
// 然後又使用這個Observer在另一個Activity中註冊在同一個LiveData上。這裏的Activity指實現LifecycleOwner的觀察者。
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 註冊觀察者
owner.getLifecycle().addObserver(wrapper);
}
繼續看前面的例子,首先在Activity中註冊通過LiveData的observe
方法進行註冊,參數傳遞了兩個,一個是this
本身,一個是Observer
接口對象。在observe方法中,首先會拿到LifecycleOwner的生命週期狀態,並且忽略處於DESTROYED
狀態的觀察者的註冊信息,而且一個觀察者還只能與一個LifecycleOwner進行綁定註冊。
然後,在實際註冊中,是將LifecycleOwner和Observer綁定在一起成爲一個wrapper
,這時候,wraper就可以看作是一個擁有聲明週期的觀察者。接下來先註冊到LiveData中,再註冊到Lifecycle
中去感知生命週期的變化。
這個wrapper是LifecycleBoundObserver
類型,它最終實現了LifecycleObserver接口,因此它可以被添加在Lifecycle中,這樣當觀察者的生命週期狀態發生變化的時候,就會通知到wrapper的onStateChanged
方法,從而去執行一系列的操作。至於爲什麼會調用到這個方法,就要自己去看一下Lifecycle的實現了。
// LiveEventObserver
public interface LifecycleEventObserver extends LifecycleObserver {
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
// LiveData.LifeCycleObserver
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
...
}
上面介紹的就是LiveData的註冊流程,簡單總結一下就是將觀察者與LifecycleOwner包裝成一個LifecycleBoundObserver
,然後同時註冊在LiveData中和Lifecycle中。註冊在LiveData是爲了去通知觀察者,註冊在Lifecycle中是爲了感知生命週期的變化。
3.2,修改LiveData的值,獲取事件通知
對LiveData的使用從前面也就看出來了,主要就是註冊成爲觀察者,然後修改LiveData的值使得觀察者獲得更新數據的通知。LiveData提供了兩個方法去修改數據,setValue/postValue
,前者只可以在主線程中修改,後者可以在子線程中修改。實際上postValue也是通過handler發送一個Runnable
,這個runnable中最終又調用了setValue去修改數據。因此下面直接分析setValue方法:
// LiveData
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
// 版本加1,這個版本是LiveData的修改版本,每次修改後版本+1,初始值在構造方法中設置,默認爲-1
// 帶值的構造方法默認爲已經修改了一次
mVersion++;
mData = value;
dispatchingValue(null);
}
static final int START_VERSION = -1;
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
setValue比較簡單,它首先記錄了新值並增加了版本號,然後調用了dispatchingValue
方法去分發新value,通知觀察者value被修改了。而版本號則是用來控制數據更新的,它每次更新數據的時候,這個版本號就會+1
,我們可以通過這個版本號獲取LiveData經歷了幾次數據的修改。另外,包裝類的觀察者也是有一個版本號字段的,它是用來判斷當前數據是否是最新的,因爲每次LiveData向觀察發送新數據事件的時候都會同步一下這個版本號,因此可以通過這個字段來判斷觀察者有沒有拿到最新的數據。
//LiveData
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// 若是已經在分發事件了,則直接返回,防止多次分發事件,如連續兩次setValue,則每個觀察者只能觸發一次事件,
// 第一次修改的值不會被分發給觀察者
if (mDispatchingValue) {
// 這個值是用來控制分發的,若是第一次setValue的時候,已經有一部分觀察者分發過事件了,則第二次setValue的時候,
//會重新重頭開始發送事件。也就是說,連續兩次的setValue有可能部分觀察者能收到兩次事件,而部分觀察者只能收到一次。
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// 若是參數非空,則只分發給參數(參數是一個觀察者)本身,否則去分發給所有的觀察者
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// 遍歷存儲觀察者的map集合,然後通知事件
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
dispatchingValue
方法進行了一系列的操作用來儘可能得保證事件不被分發多次。因爲修改value的方法只能從主線程訪問,所以爲了性能這裏沒有用其他複雜的操作去進行同步,而是簡單地使用了兩個參數進行控制。
從這個方法可以看出,dispatchingValue
只是通知LiveData去分發事件,它循環遍歷每一個觀察者,然後通過considerNotify
去實際通知具體的觀察者。
// LiveData
private void considerNotify(ObserverWrapper observer) {
// 觀察者處於非active狀態時不發送事件
if (!observer.mActive) {
return;
}
// shouldBeActive前面有說過,它是獲取觀察者的生命週期狀態,並處於STARTED和RESUMED的時候纔會返回true
// 這裏又進行了一次判斷,是爲了避免出現這種情況:生命週期已經變爲非活躍狀態了,但是還沒收到狀態變化的事件,
// 因此直接手動去將觀察者的狀態標記爲非活躍狀態,並且不發送消息
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
// 同步版本號
observer.mLastVersion = mVersion;
// 通知觀察事件
observer.mObserver.onChanged((T) mData);s
}
簡單總結一下:當LiveData的值修改後,會通過dispatchingValue
方法去決定是通知某個觀察者還是所有的觀察者(根據參數是否爲null來決定),然後通過considerNotify去具體通知某個觀察者。而considerNotify
中就會根據觀察者的生命週期狀態來決定是否發送消息(調用他的onChanged
方法)。
3.3,生命週期變化導致的事件分發
前面提到,當value修改後通知觀察者的時候,LiveData會判斷觀察者是否處於活躍狀態,通過mActive
參數來判斷。但是,我們已經看了LiveData從註冊到修改的整個流程,並沒有發現mActive的修改部分,也就是說,mActive是自動修改的。而mActive是用來判斷觀察者活躍狀態的,那肯定與觀察者的生命週期相關,並且我們註冊的時候還將包裝類wrapper註冊在了Lifecycle
中,因此,mActive肯定與Lifecycle相關。
由於這個wrapper實現了LifecycleEventObserver
接口,那麼我們知道,當Lifecycle的生命週期狀態發生變化的時候,肯定會調用onStateChanged
方法,這裏涉及到Lifecycle的知識,不知道的話可以先看一下Lifecycle的實現。
// LifecycleBoundObserver
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// DESTROYED狀態下移除註冊
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// 觸發觀察者狀態變化
activeStateChanged(shouldBeActive());
}
//LiveData
void activeStateChanged(boolean newActive) {
// 狀態一致則不修改
if (newActive == mActive) {
return;
}
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
// 若是active則+1,否則-1,這個值用來統計LiveData中註冊的觀察者的活躍的個數
LiveData.this.mActiveCount += mActive ? 1 : -1;
// 當前0個活躍觀察者,分發了一個活躍者。也就是說,LiveData的觀察者的活躍個數從0到1 的時候會觸發,默認空實現
if (wasInactive && mActive) {
onActive();
}
// 這種情況是活躍個數從1到0,也就是說當LiveData沒有活躍觀察者時調用,默認空實現
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
// 對於恢復活躍的觀察者,通知分發事件。這也就是爲什麼當觀察者每次回覆活躍狀態的時候都能接收到最新數據
// 這裏的參數是觀察者本身,因此在發送消息的時候,只會給這個觀察者發送消息
if (mActive) {
dispatchingValue(this);
}
}
在onStateChanged
中,每次收到生命週期事件的時候都會去判斷觀察者是否處於了DESTROYED
狀態,若是的話,則會將它從Lifecycle中移除(因此我們只需要向LiveData註冊觀察者就行,移除的事交給它自己去管理),否則就通過activeStateChanged
通知狀態的改變。
在activeStateChanged
中,會記錄觀察者的活躍狀態和當前活躍的觀察者的數量,並且在活躍的狀態下,去通知LiveData分發給觀察者最新的數據。這樣,每次觀察者的狀態轉化成active
的時候就能接收到最新的數據,而非active
的時候則不會收到數據的通知。
// LiveData
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();
removed.activeStateChanged(false);
}
// LifecycleBoundObserver
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
在removeObserver
中,會將觀察者從LiveData中移除,然後調用detachObserver
方法將觀察者從Lifecycle中移除。這時候就已經完全從移除了觀察者,接下來又調用一次activeStateChanged(false)
將它本身標記爲非活躍狀態從而去同步LiveData中對觀察者的活躍數量的變化。並且由於DESTROYED
的時候會自動去移除觀察者,我們就不用手動去移除觀察者,這極大的方便了開發者的編碼並且還不用擔心內存泄露問題。
3.4,observeForever忽略觀察者的生命週期
前面講了LiveData的使用,以及從源碼分析它是如何實現的。但我們註冊成爲LiveData的時候使用的是LiveData.observe(LifecycleOwner,Observer)
,這個方法要求在傳入觀察事件的時候同時傳入一個LifecycleOwner
參數,LiveData就是根據這個參數去獲取它的生命週期,並根據生命週期去選擇發送事件或者移除觀察者。
除了這種註冊方式,LiveData還提供了一種不關注生命週期的註冊方式,即通過LiveData.observeForever(Observer)
去註冊觀察者。這個方法跟前面講的observe方法相比,少了一個LifecycleOwner參數。因此對於這種方式註冊的觀察者,LiveData並不知道它對應的生命週期狀態,所以當LiveData的value發生改變的話,就會一直認爲它是處於活躍狀態的,也就會一直收到消息,直到我們手動將它移除。
// LiveData
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true);
}
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
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;
}
owner.getLifecycle().addObserver(wrapper);
}
比較兩種註冊方法,發現在observer中,包裝對象類型是LifecycleBoundObserver
,而observeForeve中,包裝對象類型卻是AlwaysActiveObserver
。並且最後並沒有將包裝類型註冊到Lifecycle中,而是通過activeStateChanged
將自己的狀態標記爲active狀態。由於AlwaysActiveObserver並沒有被註冊到Lifecycle中,因此它的活躍狀態不會發生改變,一直保持爲註冊時修改的活躍狀態。
// AlwaysActiveObserver
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
這個包裝對象太簡陋了,僅僅是將shouldBeActive直接返回true
,而在LifecycleBoundObserver中這個方法是根據生命週期狀態來決定返回true/false
的。我們知道當LiveData的value修改的時候,會遍歷它的觀察者,最終通過considerNotify
方法進行調用它的Observer的onChanged方法。
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
在considerNotify
中則是根據包裝類型的mActive和shouldBeActive方法去判斷當前觀察者是否處於活躍狀態。因爲我們在註冊的時候通過 wrapper.activeStateChanged(true)
去將包裝對象的mActive標記爲了true
,並且shouldBeActive
一直返回true
,也就是說,它一直滿足事件分發的條件,因此每次修改value的時候,它都會收到事件。
另外,我們使用observer的時候包裝類型爲LifecycleBoundObserver
,它是被註冊在Lifecycle中的,當Lifecycle的聲明週期變化的時候會調用它的onStateChanged
,而在這個方法中當Lifecycle狀態爲DESTROY
的時候就會將它本身移除註冊,因此不需要我們手動移除。但通過observeForeve註冊的包裝類型是不會註冊到Lifecycle中的,也就是說,當我們不需要這個觀察者的時候,必須手動通過調用removeObserver
去將他移除,否則可能會出現內存泄漏等情況。
MediatorLiveData
我們知道,使用LiveData必須要先給它註冊觀察者才能夠接收到數據變更帶來的通知,那麼當我們需要觀察很多數據的時候,就需要調用每個LiveData的observe進行註冊。那麼有沒有可以只註冊一個LiveData就能接收到多個LiveData的數據變更事件呢?這裏就是我們要介紹的MediatorLiveData
。
MediatorLiveData是MutableLiveData的子類,它本身允許添加多個LiveData。也就是說,觀察者可以通過它,實現只註冊一次而能觀測到多個LiveData的value變更事件。說的再多總歸不如代碼來的清晰,下面看一下示例代碼:
// MainActivity
class MainActivity : AppCompatActivity() {
private val intLiveData = MutableLiveData<Int>()
private val stringLiveData = MutableLiveData<String>()
private val mediatorLiveData = MediatorLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mediatorLiveData.addSource(intLiveData) {
println("int liveData :$it")
}
mediatorLiveData.addSource(stringLiveData) {
println("string liveData :$it")
}
mediatorLiveData.observe(this, Observer {
println("mediator LiveData :$it")
})
thread {
intLiveData.postValue(12)
Thread.sleep(1000)
stringLiveData.postValue("string")
Thread.sleep(1000)
mediatorLiveData.postValue("mediator")
}
}
}
在上面的代碼中,我們聲明瞭三個LiveData,兩個是基本的MutableLiveData,一個是MediatorLiveData。可以看到,首先通過mediatorLiveData的addSource
方法將另外兩個LiveData與對應的Observer添加到MediatorLiveData,最後才註冊MediatorLiveData的觀察者。接着啓動一個線程分別修改三個LiveData的值,打印結果如下。
2020-06-03 20:43:56.886 3744-3744/com.example.myapplication I/System.out: int liveData :12
2020-06-03 20:43:57.823 3744-3744/com.example.myapplication I/System.out: string liveData :string
2020-06-03 20:43:58.824 3744-3744/com.example.myapplication I/System.out: mediator LiveData :mediator
發現每個LiveData修改後都會觸發與之綁定的Observer
,由於LiveData是被添加到MediatorLiveData中的,那麼它修改後是否會觸發它的Observer則是由MediatorLiveData決定的,當MediatorLiveData沒有觀察者或者是觀察者都處於非活躍狀態的時候,LiveData的事件是不會被觸發的。
當然MediatorLiveData的使用情景肯定不是我們示例中的那樣,示例只是爲了演示MediatorLiveData如何使用,我們將通過這種最基本的使用去分析它做了什麼。
//MediatorLiveData
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
// 將LiveData與Observer事件綁定
Source<S> e = new Source<>(source, onChanged);
// 加入到map集合中
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()) {
// 若是有活躍的觀察者,則通過plug進行註冊
e.plug();
}
}
// LiveData
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
在addSource
中,首先將LiveData與Observer綁定在一塊然後加入到map集合中,然後hasActiveObservers
判斷MediatorLiveData是否有活躍的觀察者,這裏的hasActiveObservers是父類LiveData的一個方法,即判斷mActiveCount
是否大於0,這個參數我們在前面分析過了,會根據觀察者的狀態去+1
或-1
。也就是說在addSource中,當判斷MediatorMLiveData有活躍的觀察者的時候調用包裝類的plug
方法,否則什麼都不做。那麼包裝類Source又是什麼呢?
// MediatorLiveData.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
是MediatorLiveData的一個靜態內部類,並實現了Observer
,也就是說它是一個Observer事件對象。並且在onChanged事件的時候調用傳遞進來的觀察者的onChanged方法,也就是說它轉發了數據修改事件。並且在plug的時候,通過LiveData的observeForever
去註冊LiveData的觀察者,這種註冊方式的註冊前面也有說過,它不會去管觀察者的生命週期狀態,只要數據變化了,就會觸發觀察事件。
看了包裝類,也就看出來了它到底是怎麼做的了,首先在plug
的時候,將Source
本身註冊到添加的LiveData中,然後在onChanged的時候,去轉發事件給通過addSource傳遞的Observer
。實際上也就是相當於直接將Observer註冊到LiveData中,至於加了這一層Source,則是爲了加入註冊和移除的功能,然後交給MediatorLiveData去控制什麼時候執行。
那麼繼續回到MediatorLiveData,在addSource的時候,只有存在活躍的觀察者時纔會通過plug去註冊LiveData,那麼若是添加的時候沒有活躍的觀察者就不會註冊,那麼後來又是什麼時候去註冊的呢?
// MediatorLiveData
@CallSuper
@Override
protected void onActive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().plug();
}
}
@CallSuper
@Override
protected void onInactive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().unplug();
}
}
還記得LiveData的onActive
和onInactive
嗎?我們在前面分析LiveData的時候,知道了在觀察者Lifecycle狀態發生變化的時候,會通知包裝類LifecycleBoundObserver
的onStateChanged
方法進而調用activeStateChanged
方法。而在activeStateChanged
中,會記錄當前活躍的觀察者的個數,當個數從0到1
的時候,會調用onActive嗎,而在個數從1到0
的時候,則會調用onInactive,但是在LiveData中,這兩個方法都是一個空實現,原來是用在了這裏。
MediatorLiveData重寫了這兩個方法,在onActive中會遍歷集合去plug也就是observerForever,而在onInactive中則是遍歷集合unplug也就是removeObserver。也就是說,在MediatorLiveData擁有活躍的觀察者的時候,就會去註冊所有通過addSource添加進來的LiveData,而所有的觀察者都處於非活躍狀態的時候,就會全部移除。並且這個移除只是移除LiveData的註冊,而MediatorLiveData仍然會持有addSource加進來的LiveData,因此,當不需要的時候需要手動移除Source。
/ / MediatorLiveData
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}
從這裏可以看出,MediatorLiveData使用的時候要慎重,萬一忘記removeSouce
的話將會很容易造成內存泄露,這是因爲我們的Observer基本上都是內部類,會持有Activity或Fragment,進而被MediatorLiveData持有造成內存無法釋放。而我們前面的示例只是說明MediatorLiveData的使用方式,而不是正確的使用場景,因爲在實際中很少這樣使用。那麼該如何使用MediatorLiveData呢?先看下一個要介紹的東西。
Transformations LiveData的轉換器
Transformations
是LiveData包中提供的一個轉換工具,它比較簡單,只有兩個方法:map/switchMap
。它可以將一個LiveData(A)
轉換成另一個LiveData(B)
,並且B的值是根據A的改變而改變。語言比較蒼白,看下面的代碼示例吧:
// MainActivity
class MainActivity : AppCompatActivity() {
private val user = MutableLiveData<User>()
private val userInfo = Transformations.map(user) {
"User[${it.name},${it.age}]"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userInfo.observe(this, Observer {
println(it)
})
button.setOnClickListener {
user.value = User("張三", 12)
}
}
}
// User
data class User(var name: String, var age: Int)
在上面的代碼中,我們定義了一個User類型的LiveData 常量 user
,一個通過User類型轉換而來的String類型的LiveData常量userInfo
。我們在onCreate中只註冊了userInfo
,並且點擊按鈕修改的是user的值,然而卻能打印出 User[張三,12]。注意這裏的userInfo是通過Transformations根據user轉換而來的,此時他們是依賴關係,也就是當user的值修改後,userInfo就能收到通知,所以我們雖然沒註冊user,但仍然能夠拿到它數據變換帶來的通知。
而前面剛說完MediatorLiveData,他的作用就是可以只註冊自己就可以收到其他LiveData的值發生變化時的通知,與這裏是不是很像?實際上,Transformations
就是通過MediatorLiveData
實現的。
// Transformations
@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;
}
// Function
public interface Function<I, O> {
O apply(I input);
}
Transformations的map
方法過於簡陋了,就是定義一個MediatorLiveData,然後將需要map的LiveData添加進來。然後在LiveData的值變化時,通過map的第二個參數Function進行轉換,轉換完之後設置爲MediatorLiveData的值。因此,在我們的示例中,設置新的user
的值的時候,就會將User對象轉換成String,然後設置給userInfo
,從而可以收到通知。
sewitchMap
和map
是一樣的,也是通過MediatorLiveData去轉換的,**它和map的區別就是,map對第二個參數類型的返回值沒有要求,而switchMap要求必須返回一個LiveData類型,**而這個類型也就是switchMap
的返回值類型。此時,返回值LiveData是會觀測返回類型的值的修改。簡單說起來就是這樣:switchMap的第一個參數是用來定位的,可以根據第二個參數Function
去指向某一個LiveData
,然後返回值就就會觀測到這個LiveData,能夠收到它值修改的事件。下面看一下例子:
// MainActivity
private val userA = MutableLiveData<String>()
// 這個LiveData是用來確定指向的,當它的value修改後,就會去查找對應的LiveData然後返回
private val user = MutableLiveData<User>()
private val userInfo = Transformations.switchMap(user) {
// 這裏直接返回userA,實際應用中應該根據user的值決定返回值
userA
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userInfo.observe(this, Observer {
println(it)
})
// 修改userA的值,綁定前不會觸發userInfo的值修改,綁定後會觸發
button.setOnClickListener {
userA.value = "user A"
}
// 修改user的值,此時會觸發switchMap的第二個參數,然後返回userA,這時候userInfo就與userA綁定在一起了
button1.setOnClickListener {
user.value = User("張三",12)
}
}
示例中我們定義了兩個LiveData,然後通過switchMap去轉換user
,然後返回userA
。然後當我們先點擊button的時候,雖然修改了userA
的值,但userInfo
的值並不會修改。而當我們點擊了button1後,然後再去點button,這時候userInfo
的值就會與userA
的值一致,然後就會打印出user A。
在map中,我們userInfo是根據user的值來進行變化,而在switchMap中,user只是一箇中轉,示例中它指向了userA,此時userA的值修改後,userInfo也就會隨之而變了。
// Transformations
@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<>();
// 將switchMap的第一個參數添加到MediatorLiveData,然後每次source的值改變的時候,就會調用下方的onChanged方法
result.addSource(source, new Observer<X>() {
// 目標LiveData,也就是Function的返回值
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 調用Function
LiveData<Y> newLiveData = switchMapFunction.apply(x);
// 這種情況是上例中,按多次button1。因爲我們在Function中一直返回了userA,因此除了第一次外,其它的點擊到了這一步就會返回
if (mSource == newLiveData) {
return;
}
// 若是已經有綁定的LiveData了,就移除出去。這種情況是Function返回不同的LiveData。因爲我們的Function一直返回userA,所以不會到這一步
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
// source的值修改後,同步修改MediatorLiveData的值
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
代碼中註釋的也比較清晰了,可以看到switchMap
的第一個參數只是起到一個尋址的工具人作用,也就是每次它的值修改後,都會去尋找新的LiveData,然後幫助MediatorLiveData去與這個新的LiveData綁定。並且MediatorLiveData的值是觀測
這個新的LiveData的,每次這個新LiveData值修改後,MediatorLiveData都能獲得最新數據。
可以看出map與switchMap的區別:首先他們都返回一個MediatorLiveData,然後map是將這個MediatorLiveData的值與待轉換LiveData相關聯,每次這個LiveData值修改後,就會通過Functon去轉換它的值然後賦給MediatorLiveData;而switchMap中,MediatorLiveData是與第三個LiveData關聯的,待轉換LiveData是個工具,當待轉換LiveData的值變化後,就會通過Function去尋找第三個LiveData去返回,然後MediatorLiveData就觀測第三個LiveData,每次第三個LiveData值改變後,MediatorLiveData的值也會變成相同的。
Transformations的兩個功能都是通過MediatorLiveData實現的,由此也可以看出MediatorLiveData的使用情景了,主要是進行轉換的,可以將一個LiveData轉換成另一個LiveData。或者說他的使用場景是進行綁定,將MediatorLiveData與其他LiveData進行綁定,然後就可以監測這個LiveData。