前言
這一節我們將學習LiveData組件,學完本節之后,同學們不僅可以掌握LiveData的使用場景,消息分發原理,黏性事件產生的原理,哪怕遇到了任何問題,都可以有跡可循。
提綱
- 什么是LiveData
- LiveData衍生類
- LiveData核心方法
- LiveData事件分發實現原理
什么是LiveData
LiveData組件是Jetpack新推出基于觀察者的消息訂閱/分發組件,具有宿主(Activity/Fragment)生命周期感知能力,這種感知能力可確保LiveData僅分發消息給處于活躍狀態的觀察者,既只有處于活躍狀態的觀察者才能收到消息。
活躍狀態:通常情況下等于Observer所在宿主處于started、resumed狀態,如果使用observeForever注冊的,則一直處于活躍狀態。
LiveData的消息分發機制,是以往的Handler、EventBus、RxJavaBus無法比擬的,它們不會顧及當前頁面是否可見,一股腦的有消息就轉發。導致即便應用在后臺頁面不可見的情況下還在做一些無用的工作搶占資源。舉個例子,細心的同學可以發現微信消息列表是在頁面可見狀態時才會更新列表最新信息的。
LiveData的出現解決了以往使用callback回調可能帶來的NPE,生命周期越界,后臺任務搶占資源等問題。
從代碼的角度來看一看LiveData與傳統消息分發組件的不同:
class MainActivity extends AppcompactActivity{
public void onCreate(Bundle bundle){
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//無論頁面可見不可見,都會去執行頁面刷新,IO。更有甚者彈出對話框
}
};
//1.無論當前頁面是否可見,這條消息都會被分發。----消耗資源
//2.無論當前宿主是否還存活,這條消息都會被分發。---內存泄漏
handler.sendMessage(msg)
liveData.observer(this,new Observer<User>){
void onChanged(User user){
}
}
//1.減少資源占用--- 頁面不可見時不會派發消息
//2.確保頁面始終保持最新狀態---頁面可見時,會立刻派發最新的一條消息給所有觀察者--保證頁面最新狀態
//3.不再需要手動處理生命周期---避免NPE
//4.可以打造一款不用反注冊,不會內存泄漏的消息總線---取代eventbus
liveData.postValue(data);
}
}
LiveData的優勢
確保頁面符合數據狀態
LiveData遵循觀察者模式。當生命周期狀態發生變化時,LiveData會通知Observer對象并把最新數據派發給它。觀察者可以在收到onChanged事件時更新界面,而不是每次數據發生更改時立即更新界面。
不再需要手動處理生命周期
只需要觀察相關數據。不用手動停止或回復觀察。LiveData會自動管理Observer的反注冊,因為它能感知宿主生命周期的變化,并在宿主生命周期的onDestory自動進行反注冊。因為使用LiveData做消息分發不會發生內存泄露
數據始終保持最新狀態
如果宿主的生命周期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的數據。例如,曾經在后臺的Activity會在返回前臺后立即接收最新的數據。
數據始終保持最新狀態
如果宿主的生命周期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的數據。例如,曾經在后臺的Activity會在返回前臺后立即接收最新的數據。
支持黏性事件的分發
即先發送一條數據,后注冊一個觀察者,默認是能夠收到之前發送的那條數據
共享資源
我們可以使用單例模式拓展LiveData,實現全局的消息分發總線
LiveData的幾種用法
使用LiveData之前需要先添加依賴:
//通常情況下,只需要添加appcompat就可以了
api 'androidx.appcompat:appcompat:1.1.0'
//如果想單獨使用,可引入下面依賴
api 'androidx.lifecycle:lifecycle-livedata:2.0.0'
MutableLiveData
我們在使用LiveData做消息分發的時候,需要使用這個子類。之所以這么設計,是考慮到單一開閉原則,只有拿到MutableLive對象才可以發送消息,Livedata對象只能接受消息,避免拿到LiveData對象時既能發消息也能收消息的混亂使用。
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
MediatorLiveData
- 可以統一觀察多個LiveData發射的數據進行統一的處理。
- 同時也可以做為一個LiveData,被其他Observer觀察。
//創建兩個長得差不多的LiveData對象
LiveData<Integer> liveData1 = new MutableLiveData();
LiveData<Integer> liveData2 = new MutableLiveData();
//再創建一個聚合類MediatorLiveData
MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
//分別把上面創建LiveData 添加進來。
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);
Observer observer = new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer s) {
titleTextView.setText(s);
}
//一旦liveData或liveData發送了新的數據 ,observer便能觀察的到,以便 統一處理更新UI
Transformations.map 操作符
可以對LiveData的數據進行變化,并且返回一個新的LiveData對象,這一點了解即可。
MutableLiveData<Integer> data = new MutableLiveData<>();
//數據轉換
LiveData<String> transformData = Transformations.map(data, input -> String.valueOf(input));
//使用轉換后生成的transformData去觀察數據
transformData.observe( this, output -> {
});
//使用原始的livedata發送數據
data.setValue(10);
LiveData核心方法
方法名 | 作用 |
---|---|
observe(LifecycleOwner owner,Observer observer) | 注冊和宿主生命周期相關的觀察者 |
observeForever(Observer observer) | 注冊觀察者,不會反注冊,需自行維護 |
setValue(T data) | 發送數據,沒有活躍的觀察者時不分發,只能在主線程 |
postValue(T data) | 和setValue一樣,不受線程環境限制 |
onActive | 當且僅當有一個活躍的觀察者時才觸發 |
inActivie | 不存在活躍的觀察者時才觸發 |
LiveData實現原理
黏性消息分發流程,即新注冊的observer也能接受到前面發送的最后一條數據。原因就在于LiveData每次發送一條數據它的mVersion都會+1。但是新注冊的Observer的lastVersion=0,圖中的considerNotity方法就會把前面發送的數據分發給信注冊的Observer了。
消息分發這個流程還是比較簡單的,我就不用文字贅述了,看圖就能懂:
LiveData注冊觀察者觸發消息分發流程原理分析
observe注冊時,可以主動跟宿主生命周期綁定,不用反注冊:
observe 注冊時,可以主動跟宿主生命周期綁定,不用反注冊:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//1. 首先來個斷言,這個方法只能在主線程調用,observeForever也是。
assertMainThread("observe");
//2.其次把注冊進來的observer包裝成 一個具有生命周邊邊界的觀察者
//它能監聽宿主被銷毀的事件,從而主動的把自己反注冊,避免內存泄漏
//此時觀察者是否處于活躍狀態就等于宿主是否可見
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//3.接著會判斷該觀察是否已經注冊過了,如果是則拋異常,所以要注意,不允許重復注冊
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
//4.這一步才是關鍵
//利用Lifecycle,把觀察者注冊到進去,才能監聽到宿主生命周期狀態的變化,對不對?
//根據上節的分析,一旦一個新的觀察者被添加,Lifecycle也會同步它的狀態和宿主一致對不對?此時會觸發觀察者的onStateChanged方法
owner.getLifecycle().addObserver(wrapper);
}
LifecycleBoundObserver監聽宿主的生命周期,并且宿主不可見時不分發任何數據:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
//使用observer方法注冊的觀察者都會被包裝成LifecycleBoundObserver
//觀察者是否活躍就等于宿主 的狀態是否大于等于STARTED,
//如果頁面當前不可見,你發送了一條消息,此時是不會被分發的,可以避免后臺任務搶占資源,當頁面恢復可見才會分發。
//注意:如果使用observerForever注冊的觀察者,
//會被包裝成AlwaysActiveObserver,它的shouldBeActive一致返回true.即便在頁面不可見也能收到數據
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//在這里如果監聽到宿主被銷毀了,則主動地把自己從livedata的觀察者中移除掉
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
//否則說明宿主的狀態發生了變化,此時會判斷宿主是否處于活躍狀態
activeStateChanged(shouldBeActive());
}
}
ObserverWarpper狀態變更后,如果觀察者處于活躍狀態會觸發數據的分發流程:
abstract class ObserverWrapper{
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION//這里就等于-1,沒有主動和LiveData的mVersion對齊,為黏性事件埋下了伏筆
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
//更改觀察者的狀態
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
//如果此時有且只有一個活躍的觀察者則觸發onActive
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
//沒有任何一個活躍的觀察者則觸發onInactive
//利用這個方法被觸發的時機,可以做很多事,比如懶加載,資源釋放等
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
//如果此時觀察者處于活躍狀態,下面就開始分發數據了
//請注意,這里傳遞了this = observer
if (mActive) {
dispatchingValue(this);
}
}
}
dispatchingValue數據分發流程控制:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
//如果傳遞的觀察者不為空,則把數據分發給他自己。這個流程是新注冊觀察者的時候會被觸發
considerNotify(initiator);
initiator = null;
} else {
//否則遍歷集合中所有已注冊的的觀察者,逐個調用considerNotify,分發數據
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
considerNotity數據真正分發的地方,需要滿足三個條件:
private void considerNotify(ObserverWrapper observer) {
//觀察者當前狀態不活躍不分發
if (!observer.mActive) {
return;
}
//觀察者所在宿主是否處于活躍狀態,否則不分發,并且更改觀察者的狀態為false
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//此處判斷觀察者接收消息的次數是否大于等于 發送消息的次數
//但是observer被創建之初verison=-1
//如果此時LiveData已經發送過數據了。這里就不滿足了,就出現黏性事件了,后注冊的觀察者收到了前面發送的消息。
if (observer.mLastVersion >= mVersion) {
return;
}
//每分發一次消息,則把觀察者和LiveData的version對齊,防止重復發送
observer.mLastVersion = mVersion;
//最后的數據傳遞
observer.mObserver.onChanged((T) mData);
}
普通消息分發流程。即調用postValue,setValue才會觸發消息的分發:
總結
我們經常會使用observer(),observerForever()去注冊觀察者,它倆有什么區別呢?
- observer():不需要手動反注冊,并且宿主不可見時收不到消息,當宿主回復可見時,會立刻受到最新的數據;
- observeForever():需要自行手動反注冊,并且無論宿主是否可見,都能夠收到消息;
- 可以充分利用onActive()方法被激活的時機,來實現一些數據懶加載的功能。
留下個思考, 既然我們已經知道了LiveData產生黏性事件的原因?那么如何去解決呢?請看下回分解