EventBus 簡介
eventbus 是一個Android publish/subscrible 框架眉菱,通過解耦發(fā)布者和訂閱者,簡化Android事件傳遞塘装,從而使代碼整潔赴涵。
eventbus 3.0和2.0有區(qū)別辞色,本文基于3.0描述相關問題
源碼分析
- 創(chuàng)建eventbus
EventBus.getDefault().register 或者是 post 等
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
使用了單例模式雙重同步鎖保證線程安全,效率高
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//key:訂閱的事件,value:訂閱這個事件的所有訂閱者集合
//private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
subscriptionsByEventType = new HashMap<>();
//key:訂閱者對象,value:這個訂閱者訂閱的事件集合
//private final Map<Object, List<Class<?>>> typesBySubscriber;
typesBySubscriber = new HashMap<>();
//粘性事件 key:粘性事件的class對象, value:事件對象
//private final Map<Class<?>, Object> stickyEvents;
stickyEvents = new ConcurrentHashMap<>();
//事件主線程處理
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
//事件 Background 處理
backgroundPoster = new BackgroundPoster(this);
//事件異步線程處理
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//訂閱者響應函數(shù)信息存儲和查找類
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
//是否支持事件繼承
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
可以看出是通過初始化了一個EventBusBuilder()對象來分別初始化EventBus的一些配置,當我們在寫一個需要自定義配置的框架的時候,這種實現(xiàn)方法非常普遍,將配置解耦出去,使我們的代碼結構更清晰 這種寫法很值得學習
- register 流程
- post 流程
PostThread:默認的 ThreadMode米罚,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應方法钧汹,不論該線程是否為主線程(UI 線程)。當該線程為主線程時录择,響應方法中不能有耗時操作拔莱,否則有卡主線程的風險。適用場景:對于是否在主線程執(zhí)行無要求隘竭,但若 Post 線程為主線程塘秦,不能耗時的操作;
MainThread:在主線程中執(zhí)行響應方法动看。如果發(fā)布線程就是主線程尊剔,則直接調(diào)用訂閱者的事件響應方法,否則通過主線程的 Handler 發(fā)送消息在主線程中處理——調(diào)用訂閱者的事件響應函數(shù)菱皆。顯然须误,MainThread類的方法也不能有耗時操作,以避免卡主線程搔预。適用場景:必須在主線程執(zhí)行的操作霹期;
BackgroundThread:在后臺線程中執(zhí)行響應方法叶组。如果發(fā)布線程不是主線程拯田,則直接調(diào)用訂閱者的事件響應函數(shù),否則啟動唯一的后臺線程去處理甩十。由于后臺線程是唯一的船庇,當事件超過一個的時候,它們會被放在隊列中依次執(zhí)行侣监,因此該類響應方法雖然沒有PostThread類和MainThread類方法對性能敏感鸭轮,但最好不要有重度耗時的操作或太頻繁的輕度耗時操作,以造成其他操作等待橄霉。適用場景:操作輕微耗時且不會過于頻繁窃爷,即一般的耗時操作都可以放在這里;
Async:不論發(fā)布線程是否為主線程姓蜂,都使用一個空閑線程來處理按厘。和BackgroundThread不同的是,Async類的所有線程是相互獨立的钱慢,因此不會出現(xiàn)卡線程的問題逮京。適用場景:長耗時操作,例如網(wǎng)絡訪問束莫。
- unRegister
public synchronized void unregister(Object subscriber) {
//通過typesBySubscriber來取出這個subscriber訂閱者訂閱的事件類型,
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//分別解除每個訂閱了的事件類型
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//從typesBySubscriber移除subscriber
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//subscriptionsByEventType里拿出這個事件類型的訂閱者列表.
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
//取消訂閱
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
優(yōu)點:EventBus并不是標準的觀察者模式的實現(xiàn),但是它的整體就是一個發(fā)布/訂閱框架,也擁有觀察者模式的優(yōu)點,比如:發(fā)布者和訂閱者的解耦
缺點:EventBus是一個輕量級的東西懒棉,這個東西甚至我們自己實現(xiàn)都可以草描,而廣播雖然原理一樣,但是實際是個很復雜的東西策严。兩者還有個共同的地方穗慕,那就是需要注冊與注銷,為什么需要注銷妻导,因為其一如果你不注銷揍诽,那么在你不想接收消息的時候,例如你的頁面已經(jīng)關閉了栗竖,實際消息還是能接收到暑脆,這時候在一個已經(jīng)關閉了的頁面中執(zhí)行代碼是很不安全的;其二在注冊時EventBus保存了你訂閱者狐肢,例如Activity的引用添吗,而你不去注銷,那么EventBus就會一直保存著你的引用份名,這樣就引起了內(nèi)存泄漏碟联。EventBus和Broadcast還有個不同點,那就是Broadcast傳遞的消息只能是Intent僵腺,而Intent中除了序列化的對象外只能傳遞值鲤孵,不能傳遞引用,但是EventBus可以傳遞任何你想要的東西辰如。我們來想一想EventBus的這種特性的利弊普监。利,咱們這篇文檔一直再說琉兜,粗暴點說就是用的爽凯正,不同頁面,甚至不同模塊之間通訊是如此的省事豌蟋。而弊廊散,同樣是因為太省事了,因為我們忽略了系統(tǒng)設計的初衷梧疲≡识茫可以想想,為什么Activity之間啟動用Intent幌氮,之間想要通訊也是用Broadcast傳遞Intent缭受,而Intent又不能傳遞復雜的東西,只能是基本類型浩销,即使是序列化的贯涎,也是對象的拷貝,原因就在于慢洋,系統(tǒng)設計為了使頁面與頁面之間的耦合降到最低塘雳,使他們之間的接觸變的最少陆盘,自己的邏輯在自己模塊中寫,而不是通過對象來回傳遞败明。所以說從這一點出發(fā)隘马,EventBus又打破了這種設計的合理性,一旦使用不合理妻顶,會使代碼非常的糟糕酸员。例如頁面邏輯和業(yè)務邏輯可以隨便放置,因為只需要通過發(fā)送對象就可以讓另一個頁面另一個模塊幫你做處理讳嘱,如果工程中大量的如此使用幔嗦,維護者可能就要罵街了。所以使用要合理沥潭,呵呵邀泉。