把 LiveData 用于事件传递那些坑
0、前言
如果不是很了解 LiveData 和 Lifecycle 的同學(xué)可以先看一下我之前的文章 基于 Android Architecture Components 的 MVVM 淺析。同時(shí)安利下自己平時(shí)用的工具庫 LiveDataUtils,還在不斷完善中,歡迎大家 star、fork、關(guān)注和批評(píng)指正。
1、為什么要把 LiveData 當(dāng)作事件傳遞用
利用觀察者模式打造的事件總線的優(yōu)點(diǎn)不必多說(當(dāng)然也有很多缺點(diǎn)),如 EventBus 和 RxBus 用的好的話能起到很好的解耦作用,使整個(gè)程序架構(gòu)更加清晰,不至于到處傳遞各種 Callback。但是他們都缺少了對(duì) View 層(Activity、Fragment 等)的生命周期的感知能力,需要在生命周期結(jié)束時(shí)手動(dòng)解除觀察者,手動(dòng)管理生命周期十分繁瑣且很容易出錯(cuò)。
而 Google 推出的 Lifecycle 庫就是為了解決這一問題,其中的 LiveData 就是一個(gè)具有生命周期感知能力的觀察者,如果用它來打造一個(gè)能夠感知生命周期的事件總線,豈不美哉!
2、LiveData 當(dāng)作事件傳遞用的那些坑
在隨著對(duì) LiveData 的運(yùn)用和理解的逐漸深入,特別是對(duì)它的「生命周期感知能力」有了更深的理解,慢慢發(fā)現(xiàn)這樣用的一些坑,借此機(jī)會(huì)就跟大家分享探討一下。而且我平時(shí)也有把 LiveData 純粹當(dāng)作事件傳遞來用,特別是列表操作(比如涉及 IO 的增刪操作,View 層需要知道哪個(gè)數(shù)據(jù)改動(dòng)了,以及操作是否成功等,只能以事件的形式傳遞)。
2.1、postValue 數(shù)據(jù)丟失的問題
在我的前一篇文章中也提到過,大家也可以直接看源碼。postValue 只是把傳進(jìn)來的數(shù)據(jù)先存到 mPendingData,然后往主線程拋一個(gè) Runnable,在這個(gè) Runnable 里面再調(diào)用 setValue 來把存起來的值真正設(shè)置上去,并回調(diào)觀察者們。而如果在這個(gè) Runnable 執(zhí)行前多次 postValue,其實(shí)只是改變暫存的值 mPendingData,并不會(huì)再次拋另一個(gè) Runnable。這就會(huì)出現(xiàn)后設(shè)置的值把前面的值覆蓋掉的問題,會(huì)導(dǎo)致事件丟失。
protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;// 這里先把數(shù)據(jù)暫存起來,后來的數(shù)據(jù)會(huì)覆蓋前面的mPendingData = value;}// 這里保證只拋一個(gè) mPostValueRunnable,#-.-if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } 復(fù)制代碼2.2、setValue 不回調(diào)觀察者
LiveData 的生命周期感知能力就體現(xiàn)在這里,它不會(huì)回調(diào)處于「非激活狀態(tài)」(即 onStart 之后到 onPause 之前)的觀察者,因?yàn)檫@時(shí)更新 View 沒有意義,而且比較危險(xiǎn),它會(huì)等到觀察者激活之后再把新的值回調(diào)給他。
但是如果我傳了多個(gè)數(shù)據(jù)(假設(shè)都是用 setValue 保證不會(huì)被覆蓋),那些處于非激活狀態(tài)的觀察者是毫不知情的,他們?cè)诩せ畹臅r(shí)候只會(huì)收到最后一個(gè)數(shù)據(jù)。這對(duì)于事件傳遞來說,就表現(xiàn)為事件丟失,中間傳的任何數(shù)據(jù)都無法收到,那也就失去了事件傳遞的意義。
2.3、LiveData 就不是為傳遞事件準(zhǔn)備的
從上面兩點(diǎn)也可以看出,LiveData (或者說它的觀察者) 在觀察者激活之前并不關(guān)心中間經(jīng)歷過多少次數(shù)據(jù)變更,它只會(huì)在某個(gè)觀察者激活時(shí),傳遞給他最新的值,中間的值都不會(huì)起作用。
當(dāng)然 LiveData 的設(shè)計(jì)也不是為了傳遞事件的,它是為了反應(yīng) View 當(dāng)前狀態(tài)的,View 層只需要根據(jù)當(dāng)前 LiveData 的值去渲染數(shù)據(jù)就行,非激活狀態(tài)時(shí) View 都不可見,就算更新了也沒意義。
我最開始也是覺得 LiveData 用到了觀察者模式,而且可以進(jìn)行一些不同 Fragment 之間數(shù)據(jù)通訊,就想到了事件總線,現(xiàn)在想想當(dāng)時(shí)還是 too young too naive。
3、打造一個(gè)不會(huì)丟事件的 LiveData
LiveData 的其他功能做的很完善,只是會(huì)丟事件,我們要改造就要就針對(duì)上面的問題逐個(gè)擊破。
3.1、postValue 的問題
對(duì)于 postValue 的問題,既然它最后也是調(diào)用的 setValue,丟數(shù)據(jù)是因?yàn)橹粧伭艘淮?Runable,那我們就自己每次都往主線程拋一個(gè) Runable 就能解決這個(gè)問題 具體實(shí)現(xiàn)可以參考之前提到的 LiveDataUtils。
/*** LiveData 相關(guān)的工具類,簡(jiǎn)化 LiveData 操作** @author funnywolf* @since 2019-04-22*/ public class LiveDataUtils {private static Handler sMainHandler;/*** 用 setValue 更新 MutableLiveData 的數(shù)據(jù),如果在子線程,就切換到主線程*/public static <T> void setValue(MutableLiveData<T> mld, T d) {if (mld == null) {return;}if (Thread.currentThread() == Looper.getMainLooper().getThread()) {mld.setValue(d);} else {postSetValue(mld, d);}}/*** 向主線程的 handler 拋 SetValueRunnable*/public static <T> void postSetValue(MutableLiveData<T> mld, T d) {if (sMainHandler == null) {sMainHandler = new Handler(Looper.getMainLooper());}sMainHandler.post(SetValueRunnable.create(mld, d));}private static class SetValueRunnable<T> implements Runnable {private final MutableLiveData<T> liveData;private final T data;private SetValueRunnable(@NonNull MutableLiveData<T> liveData, T data) {this.liveData = liveData;this.data = data;}public void run() {liveData.setValue(data);}public static <T> SetValueRunnable<T> create(@NonNull MutableLiveData<T> liveData, T data) {return new SetValueRunnable<>(liveData, data);}} } 復(fù)制代碼3.2、非激活狀態(tài)的問題
其實(shí)我覺得這個(gè)問題的主要「責(zé)任」并不在 LiveData,而是在它的觀察者,「是你告訴我你非激活的呀,那我怎么給你發(fā)數(shù)據(jù)呢,我發(fā)給你,萬一你出問題了呢,那到底誰負(fù)責(zé)?」。
我們常用的觀察者其實(shí)是 LifecycleBoundObserver,在調(diào)用 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) 會(huì)自動(dòng)幫我們封裝一個(gè)這樣的觀察者,而它會(huì)根據(jù) LifecycleOwner 的生命周期呈現(xiàn)出「激活」和「非激活」?fàn)顟B(tài)。
LiveData 默認(rèn)的還有另外一種觀察者 AlwaysActiveObserver,它是我們?cè)谡{(diào)用 public void observeForever(@NonNull Observer<? super T> observer) 時(shí)生成的,顧名思義它會(huì)一直處于激活狀態(tài),LiveData 當(dāng)然也不會(huì)替我們管理這樣觀察者的生命周期,我們需要在不使用時(shí)手動(dòng)調(diào)用 public void removeObserver(@NonNull final Observer<? super T> observer) 移除觀察者,否則可能會(huì)內(nèi)存泄漏。
// AlwaysActiveObserver boolean shouldBeActive() {return true; } 復(fù)制代碼這個(gè) AlwaysActiveObserver 看樣子能夠解決我們的問題,他一直處于激活狀態(tài),那所有的事件都會(huì)回調(diào)給他,但是需要我們自己管理生命周期。這不是開歷史倒車嗎?好不容易有生命周期感知了,現(xiàn)在又要自己手動(dòng)搞?
3.3、造一個(gè)生命周期感知的還不丟事件的觀察者
手動(dòng)管理生命周期是絕對(duì)不能忍的,AlwaysActiveObserver 可以解決剛才說的問題,那我們就造一個(gè)新的觀察者來管理 observeForever 和 removeObserver 的問題。既然要造,那就造個(gè)好用的,首先事件一定不能丟,要不就沒意義了;而且生命周期要觀察者自己管理,不能只是簡(jiǎn)單的 observeForever 和 removeObserver,非激活狀態(tài)之類的也要考慮進(jìn)去。
既然要管理生命周期,那就不得不用到 LifecycleOwner、Lifecycle,然后自己觀察 LifecycleOwner 的 Lifecycle。
Lifecycle 對(duì)外只給了這個(gè)接口,并不含有任何回調(diào),我們需要用注釋里說的 OnLifecycleEvent 注解來標(biāo)記相應(yīng)的函數(shù),Lifecycle 會(huì)通過反射拿到標(biāo)記的函數(shù),然后生成對(duì)應(yīng)的適配器,感興趣的可以看下 Lifecycling.getCallback 函數(shù)。比如我們可以這樣用
public class Observer implements LifecycleObserver {(Lifecycle.Event.ON_START)private void onStart() {doSomethingOnStart();} } 復(fù)制代碼拿到生命周期后,我們就可以在一開始 observeForever,在 Lifecycle.Event.ON_DESTROY 時(shí) removeObserver。
接下來就要考慮激活和非激活的狀態(tài)了,既然用了 observeForever,那每次事件都會(huì)有回調(diào),這時(shí)候如果 Lifecycle 處于激活狀態(tài),那可以直接把事件發(fā)出去。但如果非激活,不能直接把事件發(fā)出去,又不能丟,那我們就需要先把事件存起來,然后在 Lifecycle 變?yōu)榧せ顮顟B(tài)時(shí)再把存起來的事件發(fā)送出去。簡(jiǎn)單畫了下流程圖。
3.4、保證 LiveData 的事件更新
3.1 也說過要自己處理 postValue,其次要保證用我們自己定義的觀察者,需要重寫 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)。
/*** 用作事件總線的 {@link MutableLiveData}** @see AsEventBus** @author funnywolf* @since 2019-05-18*/ public class EventMutableLiveData<T> extends MutableLiveData<T> {public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {LiveEventObserver.bind(this, owner, observer);}public void postValue(T value) {LiveDataUtils.setValue(this, value);} }/*** 用作事件總線的 {@link MediatorLiveData}** @see AsEventBus** @author funnywolf* @since 2019-05-18*/ public class EventMediatorLiveData<T> extends MediatorLiveData<T> {public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {LiveEventObserver.bind(this, owner, observer);}public void postValue(T value) {LiveDataUtils.setValue(this, value);} }/*** 該注解只用于 {@link androidx.lifecycle.LiveData},用于表示 LiveData 是當(dāng)成事件總線用的,需要注意:* - 觀察者在非激活狀態(tài)(onStart 之后,onPause 之前)時(shí)不會(huì)產(chǎn)生回調(diào),會(huì)丟失事件* - postValue 可能會(huì)被覆蓋,只能用 setValue 來更新值* - LiveData 的事件都是黏性的,不使用時(shí)手動(dòng)拋出一個(gè) null 事件,以防下次綁定時(shí)會(huì)發(fā)送存在之前的舊數(shù)據(jù);** @see LiveDataUtils* @see LiveEventObserver* @see EventMutableLiveData* @see EventMediatorLiveData** @author funnywolf* @since 2019-05-06*/ (ElementType.FIELD) public AsEventBus { } 復(fù)制代碼4、LiveDataUtils 中其他工具簡(jiǎn)介
這個(gè)工具包里還有其他一些我平時(shí)常用的小工具,這里簡(jiǎn)單分享下:
- StateData 是含有狀態(tài)和錯(cuò)誤信息的數(shù)據(jù)包裝類,因?yàn)?LiveData 只有一個(gè) onChanged 回調(diào),無法知道數(shù)據(jù)狀態(tài),所以搞了這個(gè)類
- RxLiveData 繼承自 MutableLiveData,實(shí)現(xiàn)了 Disposable 和 Observer 接口,主要為了數(shù)據(jù)從 RxJava 到 LiveData 的轉(zhuǎn)換
- LiveDataBus,一個(gè)基于 LiveData 的事件總線,但是不推薦用。事件總線這玩意盡量不要用,除非是不用不行的場(chǎng)合,寫的時(shí)候很香,之后維護(hù)起來很麻煩
5、總結(jié)
不得不說谷歌的生命周期庫真的很強(qiáng)大,不僅給我們提供了現(xiàn)成的工具,還給予了我們方便 DIY 的能力,一個(gè)不到五百行的 LiveData 都能玩出很多花樣。以上也只是自己的一些經(jīng)驗(yàn)總結(jié),難免會(huì)有不足,歡迎各位批評(píng)指正。
轉(zhuǎn)載于:https://juejin.im/post/5cdff0de5188252f5e019bea
總結(jié)
以上是生活随笔為你收集整理的把 LiveData 用于事件传递那些坑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux学习记录-7
- 下一篇: AJPFX实例集合嵌套之ArrayLis