前言
之前的文章中我們介紹了LiveData的內(nèi)部實現(xiàn)遣耍,并提出LiveData也可用作應(yīng)用程序內(nèi)的通信手段。而目前炮车,網(wǎng)絡(luò)上也確實有很多相關(guān)的實現(xiàn)文章舵变。在眾多實現(xiàn)中酣溃,來自美團(tuán)的實現(xiàn)是質(zhì)量最有保證的。但是美團(tuán)的實現(xiàn)不止是使用LiveData纪隙,還涉及廣播赊豌、跨進(jìn)程通信等;本文將從美團(tuán)的實現(xiàn)中抽取LiveData相關(guān)的內(nèi)容绵咱,重新構(gòu)建一個通信框架(畢竟跨進(jìn)程等通信手段并不是每個項目都會使用到)碘饼。如果有對LiveData內(nèi)部機(jī)制不了解的同學(xué),強(qiáng)烈建議先閱讀上篇文章《LiveData為何這么香悲伶,這些你都知道嗎艾恼?》再來看本文!
推薦
文章將率先在公眾號「Code滿滿」與個人博客「李益的小站」上發(fā)布麸锉,如果本文對你有幫助钠绍,就關(guān)注一下公眾號吧!
一花沉、需要做什么
在開始正式的代碼講解前柳爽,我們需要知道我們要做什么。在LiveData中共有兩種訂閱方式:observe()
與observeForever()
碱屁。其中observeForever()
的訂閱非常簡單磷脯,觀察者永遠(yuǎn)接收消息,中間不做其他判斷娩脾,基本沒有什么可以改動的地方赵誓。而observe()
會對觀察者所在頁面的狀態(tài)進(jìn)行判斷,如果頁面處于活躍狀態(tài)晦雨,則接收數(shù)據(jù)更新架曹,否則不接收。顯而易見闹瞧,observe()
對頁面狀態(tài)進(jìn)判斷的部分绑雄,是我們可以放手施為的地方。
了解了LiveData的兩種訂閱方式后奥邮,我們再來思考一下我們的通信框架應(yīng)該如何搭建:
- 為了方便使用万牺,這個框架最好是個單例
- 為了可以靈活控制頁面是否處于活躍狀態(tài)(這會影響觀察者在何時可以接收到消息),需要重寫LiveData中對頁面活躍狀態(tài)的判斷
- 為了靈活的控制事件洽腺,需要有個池子來存儲事件脚粟,這個池子可以是Map,當(dāng)然有存儲也肯定需要有刪除
- LiveData原來只是提供了observe與observeForever方法蘸朋,缺少類似EventBus中的粘性事件核无,我們需要補(bǔ)上
以上大致就是我們需要在搭建框架時需要注意的一些點(diǎn),下面我們一點(diǎn)一點(diǎn)來實現(xiàn)藕坯。
二团南、XLiveData
我們先從修改LiveData中對頁面活躍狀態(tài)的判斷開始噪沙。我們新建一個類,命名為XLiveData吐根,此類主要用于重寫LiveData中判斷頁面是否處于活躍狀態(tài)的相關(guān)方法正歼,所以XLiveData需要繼承于MutableLiveData。
public class XLiveData<T> extends MutableLiveData<T> {
......
}
1. XLifecycleBoundObserver
在XLiveData中拷橘,定義一個繼承于LifecycleBoundObserver的內(nèi)部類局义,命名為XLifecycleBoundObserver,用于代替LiveData內(nèi)部的LifecycleBoundObserver冗疮。
protected Lifecycle.State observerActiveLevel() {
return STARTED;
}
private class XLifecycleBoundObserver extends LifecycleBoundObserver {
XLifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
super(owner, observer);
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(observerActiveLevel());
}
}
XLifecycleBoundObserver與LifecycleBoundObserver相比萄唇,主要是重寫了shouldBeActive()
方法。LifecycleBoundObserver中的shouldBeActive()
方法是當(dāng)Activity處于onStart()與onPause()生命狀態(tài)之間時赌厅,判定頁面處于活躍狀態(tài)穷绵,返回true。此處改為我們可以自行控制特愿,更加靈活仲墨,例如我們可以改為Activity處于onCreate()與onStop()生命狀態(tài)之間時,判定頁面處于活躍狀態(tài)揍障,返回true目养。
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(observerActiveLevel());
}
2. 重寫observe
既然重寫了一個XLifecycleBoundObserver,那么LiveData的observe()
方法也必須得重寫毒嫡,主要是將其中的LifecycleBoundObserver替換為XLifecycleBoundObserver癌蚁。
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// super.observe(owner, observer);
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
try {
LifecycleBoundObserver wrapper = new XLifecycleBoundObserver(owner, (Observer<T>) observer);
LifecycleBoundObserver existing = (LifecycleBoundObserver) callMethodPutIfAbsent(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);
} catch (Exception e) {
e.printStackTrace();
}
}
三、BusObservable
BusObservable是一個接口兜畸,定義了被觀察者需要實現(xiàn)的方法努释。因為真正編寫被觀察者代碼時,代碼如果過多咬摇,閱讀體驗不會太好伐蒂,影響我們對被觀察者的理解,此處先用接口定義結(jié)構(gòu)肛鹏,方便我們理解逸邦。
public interface BusObservable<T> {
/**
* 發(fā)送消息
*
* @param value 發(fā)送的消息
*/
void post(final T value);
/**
* 延遲發(fā)送消息
*
* @param value 發(fā)送的消息
* @param delay 延遲的毫秒數(shù)
*/
void postDelay(final T value, final long delay);
/**
* 延遲發(fā)送,帶生命周期
* 如果延時發(fā)送消息的時候sender處于非激活狀態(tài)在扰,消息取消發(fā)送
*
* @param owner 消息發(fā)送者
* @param value 發(fā)送的消息
* @param delay 延遲毫秒數(shù)
*/
void postDelay(@NonNull final LifecycleOwner owner, final T value, final long delay);
/**
* 發(fā)送消息
* 強(qiáng)制接收到消息的順序和發(fā)送順序一致
*
* @param value 發(fā)送的消息
*/
void postOrderly(final T value);
/**
* 注冊一個Observer缕减,帶有生命周期感知,自動取消訂閱
* 在注冊之前的發(fā)送消息芒珠,在注冊時不會被接收
*
* @param owner LifecycleOwner
* @param observer 觀察者
*/
void observe(@NonNull final LifecycleOwner owner, @NonNull final Observer<T> observer);
/**
* 注冊一個Observer桥狡,帶有生命周期感知,自動取消訂閱
* 在注冊之前發(fā)送的消息,在注冊時會被接收(消息同步)
*
* @param owner LifecycleOwner
* @param observer 觀察者
*/
void observeSticky(@NonNull final LifecycleOwner owner, @NonNull final Observer<T> observer);
/**
* 注冊一個Observer裹芝,需手動解除綁定
* 在注冊之前的發(fā)送消息呈宇,在注冊時不會被接收
*
* @param observer 觀察者
*/
void observeForever(@NonNull final Observer<T> observer);
/**
* 注冊一個Observer,需手動解除綁定
* 在注冊之前發(fā)送的消息局雄,在注冊時會被接收(消息同步)
*
* @param observer 觀察者
*/
void observeStickyForever(@NonNull final Observer<T> observer);
/**
* 通過Forever方式注冊的,需要調(diào)用該方法取消訂閱
*
* @param observer 觀察者
*/
void removeObserver(@NonNull final Observer<T> observer);
/**
* 訂閱了此BusObservable的Observer的所在頁面的生命周期狀態(tài)是否一直處于活躍狀態(tài)
*
* @param alwaysBeActive {@code true}-Observer可以在Activity的onCreate到onStop之間的生命周期狀態(tài)接收消息
* {@code false}-Observer可以在Activity的onStart到onPause之間的生命周期狀態(tài)接收消息
*/
BusObservable<T> alwaysBeActive(final boolean alwaysBeActive);
/**
* 當(dāng)BusObservable的所有的Observer都被移除時存炮,BusObservable對應(yīng)的Event是否從事件總線中移除
*
* @param autoClear
*/
BusObservable<T> autoClear(final boolean autoClear);
}
四炬搭、LiveBusCore
在進(jìn)行上述一系列鋪墊后,我們可以開始編寫核心的邏輯代碼了穆桂。首先我們先自定義一個類宫盔,命名為LiveBusCore,此類將是我們構(gòu)建的通信框架的核心代碼所在享完。
1. ObserverWrapper
我們在LiveBusCore中灼芭,定義一個內(nèi)部類ObserverWrapper,此類實現(xiàn)了Observer接口般又,主要用于代替LiveData的observe()
與observeForever()
方法中傳入的Observer對象彼绷。ObserverWrapper在調(diào)用數(shù)據(jù)更新方法onChanged(T t)
時,在內(nèi)部比Observer多加了一層是否接受更新的判斷茴迁。
private class ObserverWrapper<T> implements Observer<T> {
@NonNull
private Observer<T> observer;
// 是否拒收消息
private boolean isRejectEvent = false;
private ObserverWrapper(@NonNull Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(T t) {
if (isRejectEvent) {
isRejectEvent = false;
return;
}
observer.onChanged(t);
}
}
2. LiveEvent
在LiveBusCore中自定義一個內(nèi)部類寄悯,命名為LiveEvent,此類會實現(xiàn)上述提到的BusObservable接口堕义,所以LiveEvent就是我們的被觀察者猜旬,也是我們通信框架中的事件對象。作為一個事件倦卖,其需要持有LiveData洒擦、用于標(biāo)識自己的key以及一個用于存儲自己觀察者的Map。
private class LiveEvent<T> implements BusObservable<T> {
@NonNull
private final Object key;
private final InternalLiveData<T> liveData;
private final Map<Observer, ObserverWrapper<T>> observerMap;
private final Handler mainHandler;
public LiveEvent(@NonNull Object key) {
this.key = key;
this.liveData = new InternalLiveData<>();
this.observerMap = new HashMap<>();
this.mainHandler = new Handler(Looper.getMainLooper());
}
......
}
2.1 InternalLiveData
雖然之前我們已經(jīng)定義了一個XLiveData怕膛,但是XLiveData只是個基類熟嫩,我們需要按照我們的實際需求再定義一個符合我們需求的LiveData。因為默認(rèn)的LivData的觀察者是在頁面處于onStart()與onPause()之間才能收到消息嘉竟,我們在最大可能的避免內(nèi)存泄漏的情況下邦危,可以增加一個使觀察者接收消息的區(qū)間變?yōu)閛nCreate()與onStop()之間的選擇,方便我們做更多的操作舍扰。
private class InternalLiveData<T> extends XLiveData<T> {
private boolean observerAlwaysBeActive = false;
private boolean autoClear = true;
@Override
protected Lifecycle.State observerActiveLevel() {
return observerAlwaysBeActive ? Lifecycle.State.CREATED : Lifecycle.State.STARTED;
}
@Override
public void removeObserver(@NonNull Observer<? super T> observer) {
super.removeObserver(observer);
if (autoClear && !liveData.hasObservers()) {
LiveBusCore.getInstance().mBus.remove(key);
}
}
}
InternalLiveData除了重寫了observerActiveLevel()
方法倦蚪,還重寫了removeObserver()
方法,添加了是否會自動從事件池中移除事件的配置边苹。
2.2 發(fā)送消息
我們在LiveEvent中定義一個發(fā)送消息方法陵且,如下:
@MainThread
private void postInternal(T value) {
liveData.setValue(value);
}
上述私有方法是直接調(diào)用了LiveData的setValue方法,只能在主線程中調(diào)用,我們還需考慮到在非主線程中調(diào)用的情況慕购,最后形式如下:
@Override
public void post(final T value) {
if (isMainThread()) {
postInternal(value);
} else {
mainHandler.post(new PostValueTask(value));
}
}
private class PostValueTask implements Runnable {
private Object newValue;
public PostValueTask(@NonNull Object newValue) {
this.newValue = newValue;
}
@Override
public void run() {
postInternal((T) newValue);
}
}
2.3 普通訂閱
知道了如何發(fā)送消息聊疲,我們再看下如何訂閱消息。在接收消息時沪悲,我們需要使用ObserverWrapper來代替Observer傳遞給LiveData获洲,然后做下判斷,ObserverWrapper只有在liveData.getVersion() > XLiveData.START_VERSION;
時殿如,isRejectEvent才為true贡珊;其意義在于觀察者不接收訂閱之前的消息,只接收訂閱后的消息涉馁。
另外门岔,對于訂閱的調(diào)用,我們同樣做了在主線程和非主線程中調(diào)用的處理烤送。
@MainThread
private void observeInternal(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
ObserverWrapper<T> observerWrapper = new ObserverWrapper<>(observer);
observerWrapper.isRejectEvent = liveData.getVersion() > XLiveData.START_VERSION;
liveData.observe(owner, observerWrapper);
}
@Override
public void observe(@NonNull final LifecycleOwner owner, @NonNull final Observer<T> observer) {
if (isMainThread()) {
observeInternal(owner, observer);
} else {
mainHandler.post(new Runnable() {
@Override
public void run() {
observeInternal(owner, observer);
}
});
}
}
2.4 粘性事件
粘性事件是我們在開發(fā)中非常常用的一種事件寒随,LiveData并沒有給我們實現(xiàn),一般都會用observeForever()
方法來代替實現(xiàn)帮坚,但是每次訂閱后妻往,我們還必須手動取消訂閱,如果忘記取消试和,可能會引發(fā)內(nèi)存泄漏問題蒲讯,這是非常不友好的。我們此處可以手動實現(xiàn)一下:
@MainThread
private void observeStickyInternal(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
ObserverWrapper<T> observerWrapper = new ObserverWrapper<>(observer);
liveData.observe(owner, observerWrapper);
}
@Override
public void observeSticky(@NonNull final LifecycleOwner owner, @NonNull final Observer<T> observer) {
if (isMainThread()) {
observeStickyInternal(owner, observer);
} else {
mainHandler.post(new Runnable() {
@Override
public void run() {
observeStickyInternal(owner, observer);
}
});
}
}
可以看到灰署,observeStickyInterna()
方法與observeInternal()
方法基本相同判帮,唯一的區(qū)別就是observeStickyInterna()
中未對observerWrapper的isRejectEvent屬性做處理,即觀察者可以收到訂閱之前的消息溉箕。
2.5 removeObserver
移除觀察者就比較容易了晦墙,只需要在移除LiveData的觀察者時,同時從LiveEvent的觀察者集合中移除掉對應(yīng)的觀察者肴茄。
@MainThread
private void removeObserverInternal(@NonNull Observer<T> observer) {
Observer<T> realObserver;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
liveData.removeObserver(realObserver);
}
@Override
public void removeObserver(@NonNull final Observer<T> observer) {
if (isMainThread()) {
removeObserverInternal(observer);
} else {
mainHandler.post(new Runnable() {
@Override
public void run() {
removeObserverInternal(observer);
}
});
}
}
3. 單例
上述內(nèi)容已經(jīng)介紹了消息發(fā)送晌畅,普通訂閱、粘性事件以及移除觀察者寡痰,在我們定義的被觀察者接口中還有延遲發(fā)送消息抗楔、永久訂閱、永久粘性訂閱等方法拦坠,這些方法與我們上述的實現(xiàn)都是大同小異连躏,原理都是相同的,我們此處就不再贅述贞滨。最后我們看下單例的實現(xiàn):
public class LiveBusCore {
public static LiveBusCore getInstance() {
return Holder.DEFAULT_BUS;
}
private static class Holder {
private static final LiveBusCore DEFAULT_BUS = new LiveBusCore();
}
private Map<Object, LiveEvent> mBus;
private LiveBusCore() {
this.mBus = new HashMap<>();
}
public synchronized <T> BusObservable<T> with(Object key, Class<T> eventType) {
if (!mBus.containsKey(key)) {
mBus.put(key, new LiveEvent<T>(key));
}
return (BusObservable<T>) mBus.get(key);
}
public synchronized void removeEvent(@NonNull Object key) {
if (!mBus.containsKey(key)) {
mBus.remove(key);
}
}
......
}
為了方便使用入热,我們可以再封裝一層:
public final class LiveBus {
public static <T> BusObservable<T> get(@NonNull Object key, Class<T> type) {
return LiveBusCore.getInstance().with(key, type);
}
public static <T> BusObservable<T> get(@NonNull Class<T> eventType) {
return LiveBusCore.getInstance().with(eventType.getName(), eventType);
}
public static BusObservable<Object> get(@NonNull Object key) {
return LiveBusCore.getInstance().with(key, Object.class);
}
public static void removeEvent(@NonNull Object key) {
LiveBusCore.getInstance().removeEvent(key);
}
}
使用示例:
// 發(fā)送消息
LiveBus.get(Constants.LK_TEST2_POST).post("收到來自Test2的消息")
// 接收消息
LiveBus.get(Constants.LK_TEST2_POST, String::class.java)
.observe(this, object : Observer<String> {
override fun onChanged(t: String?) {
viewModel.normalObserveData.value = "普通訂閱:${t}"
}
})
五、小結(jié)
使用LiveData搭建通信框架的講解就到此結(jié)束了,有興趣的同學(xué)可以去我的Github上下載源碼勺良,項目是 Fly-Android(https://github.com/albert-lii/Fly-Android)绰播。如果在項目中沒有跨進(jìn)程通信等特殊需求的話,建議自己使用LiveData搭建通信框架尚困,這樣更加靈活蠢箩,易修改。最后給出美團(tuán)的框架:LiveEventBus事甜。