在安卓中處理不一樣組件之間的事件傳遞依靠廣播機制,即Intent/BroadcastReceiver機制
,其原理相似於傳感網中的Ad hoc網絡模式,全部組件處在一種無序狀態;android
事件總線機制則引入中心控制節點來集中管理事件,相似於移動通訊網絡中的基站功能。git
總線這個概念來自於計算機,計算機中各類功能部件如CPU,顯卡之類不會採用兩兩互聯的方式,那樣佈線會很是複雜,實際是使用總線做爲公共通訊幹線,掛載上全部的功能部件。github
事件總線框架採用訂閱/發佈模型網絡
在這個模型中包含如下元素框架
1.事件類,這是要傳遞的事件對象。
2.事件總線,是中心控制節點,負責管理事件的註冊,接收發布的事件,並完成事件匹配以進行事件處理。
3.發佈者,負責分發事件。
4.訂閱者,負責事件處理並註冊到事件總線。異步
事件總線模型比廣播機制的優點在於async
1.簡化了組件之間的通訊
2.實現了事件分發和事件處理的解耦,所以兩者能夠在不一樣線程中實現。
3.擴展了事件類,實際上事件類就是根類Object
,而沒必要藏身在Intent
中了。ide
事件總線模型不能徹底替代廣播機制,由於廣播機制能夠完成跨App間組件調用。oop
EventBus
採用發佈/訂閱模式,想要接收事件必須先訂閱總線,這與手機要註冊基站相似。EventBus
的做用在於優化Activities, Fragments等之間的通訊,並處理可以線程問題。post
EventBus
的使用很是簡單,包括如下步驟
1.總線的創建。
EventBus eventBus = EventBus.getDefault();
毫無疑問,事件總線對象應該是單例實現,若是要對其進行初始化配置最好放在Applicaiton
類中進行。
2.建立事件,事件就是普通的Java
類,如
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
3.建立訂閱方法。訂閱者其實是處理事件的方法,以onEvent
來命名。
public void onEvent(MessageEvent event){ Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); }
4.訂閱者註冊和取消。將包含訂閱方法的類註冊到總線。
protected void onCreate(Bundle savedInstanceState) { //。。。 EventBus.getDefault().register(this,1); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().register(this); }
5.事件發佈。
EventBus.getDefault().post(new MessageEvent("MessageEvent"));
關於線程切換要注意在事件分發是處在當前線程中的,是比較容易控制的。EventBus
主要簡化的是事件處理所處的線程。共有四種線程切換方法,其區別在於事件處理方法的命名。
onEvent
方法,爲默認PostThread
模式。處理線程就是分發線程。
onEventMainThread
方法,主線程模式。處理線程最終爲UI線程,與分發線程無關。
onEventBackgroundThread
方法,背景線程模式。處理線程最終爲背景線程。這意味着若是分發線程是UI線程,則將新建一背景線程做處理線程;若是分發線程不是UI線程,則分發線程就用做處理線程。
onEventAsync
方法,異步模式。無論分發線程如何,處理線程都將新建一線程,底層採用線程池技術實現。
Otto
是Square
推出的事件總線框架,基於Guava
框架。Guava
框架查找訂閱方法採用遍歷類方法,Otto
則是使用註解來查找,雖然EventBus
在初始版本中也採用註解訂閱方法,但由於性能問題改成按照方法名查找。
Otto
的使用與EventBus
相似,最大區別在因而否對訂閱方法使用註解。
Bus bus = new Bus(ThreadEnforcer.MAIN); @Subscribe public void answerAvailable(AnswerAvailableEvent event){ // TODO: React to the event somehow! } bus.register(this); bus.post(new AnswerAvailableEvent(42));
能夠看到在功能和性能上EventBus都完勝Otto。
首先要理解幾個概念,先看看訂閱方法
void onEvent(MessageEvent event)
訂閱方法中包含一個具體事件類做參數,並經過重載實現不一樣的訂閱方法。
SubscriberMethod
類表示單個訂閱方法,主要域包括
Method method;//訂閱方法的反射表示,實現ding'y方法的 ThreadMode threadMode;//處理事件所用線程模型 Class<?> eventType;//具體事件類類型
Subscription
類也表示訂閱方法,是SubscriberMethod
類的進一步封裝,主要域包括
Object subscriber; //具體事件類對象 SubscriberMethod subscriberMethod; int priority; //訂閱方法優先級
總的說來,事件總線的原理是在總線中維持兩個集合:一個表示訂閱方法集合,一個表示事件類型集合。
註冊訂閱方法分爲兩步:首先查找全部出全部訂閱方法;其次將訂閱方法及其事件類型分別加入總線的兩個集合中。
事件分發時,須要根據事件對象提取出事件類型,然後構建訂閱方法,在總線兩個集合中分別查找是否存在該事件;若是存在就按照線程模型分別執行。
PostingThreadState
List<Object> eventQueue = new ArrayList<Object>(); boolean isPosting; boolean isMainThread; Subscription subscription;//訂閱類 Object event; //事件類型 boolean canceled;
1.EventBus
建立使用getDefault()
方法採用單例模式,也能夠經過buidler
指定,須要同步建立。
2.訂閱方法註冊
EventBus.getDefault().register(this);
總的說來方法訂閱包括兩步:
這裏注意形式上註冊到總線的是MainActivity
對象,並非具體訂閱方法,因此存在一個查找出活動對象中全部訂閱方法的過程。
同時總線中要維持兩個集合:訂閱類集合和事件類型集合。新的訂閱方法要添加到這兩個集合中。
private synchronized void register(Object subscriber, boolean sticky, int priority) { //1.首先對活動類查找出其包含的訂閱方法(SubscriberMethod)集合 List<SubscriberMethod> subscriberMethods = findSubscriberMethods(subscriber.getClass()); //2.然後將對每個訂閱方法(SubscriberMethod)註冊 for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); } }
註冊訂閱方法(SubscriberMethod)簡化版以下,不考慮striky
狀況,完成將一個訂閱方法A插入總線集合中:
//Object subscriber 如 MainActivity //SubscriberMethod 訂閱方法,如onEvent private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { //獲取訂閱方法A中的事件類型A.eventType,如MessageEvent Class<?> eventType = subscriberMethod.eventType; //根據事件類型A.eventType獲取總線中已經存在的訂閱方法(Subscription)集合S CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //根據訂閱方法A建立其對應的封裝訂閱方法B Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); //在訂閱方法集合S中查找封裝訂閱方法B //首先要處理S=NULL的狀況,此時要建立S,並將B插入S。 //若是S已經包含B,拋出異常,即不能重複註冊同一個訂閱方法。 if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<Subscription>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //若是S不爲空且B不在S中,要將B按照優先級順序插入到S int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || newSubscription.priority > subscriptions.get(i).priority) { subscriptions.add(i, newSubscription); break; } } //總線中維護一個事件類型集合,還須要將新事件類型A.eventType加入該集合 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<Class<?>>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); }
3.事件分發方法簡化版爲
public void post(Object event) { //分發事件時將具體事件入隊 PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); //處理隊列中優先級最高的事件 while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0), postingState); } }
//Object event 事件類對象 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { //獲取具體事件類對象event對應的事件類型E,如MessageEvent Class<?> eventClass = event.getClass(); //在事件總線集合中查找是否存在具體事件類型E,若是不存在,則分發NoSubscriberEvent事件;若是存在,繼續分發。 boolean subscriptionFound = false; subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); }
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { //根據事件類型同步獲取總線中的封裝訂閱方法集合S,這裏要注意某個具體事件類型可能有多個線程版本 CopyOnWriteArrayList<Subscription> subscriptions; //遍歷S中訂閱方法,進行事件處理 if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postToSubscription(subscription, event, postingState.isMainThread); } return true; } return false; }
根據ThreadMode類型處理事件
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case PostThread: invokeSubscriber(subscription, event); break; case MainThread: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BackgroundThread: if (isMainThread) { backgroundPoster.enqueue(subscription, event); } else { invokeSubscriber(subscription, event); } break; case Async: asyncPoster.enqueue(subscription, event); break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); } }
4.幾種線程有關的事件處理方法PostThread
方式採用反射完成事件處理。
void invokeSubscriber(Subscription subscription, Object event) { subscription.subscriberMethod.method.invoke(subscription.subscriber, event); }
MainThread
模式在異步狀況下采用Handler
完成事件處理,具體類爲HandlerPoster
類,這個類採用Looper.getMainLooper()
構造以保證事件處理執行在主線程中。
mainThreadPoster.enqueue(subscription, event);
void enqueue(Subscription subscription, Object event) { //構造一個PendingPost類並將其入隊 PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); queue.enqueue(pendingPost); }
BackgroundThread與Async模式採用線程池來執行,
eventBus.getExecutorService() = Executors.newCachedThreadPool();
public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); queue.enqueue(pendingPost); eventBus.getExecutorService().execute(this); }
greenrobot-EventBus-HOWTO.md
EventBus for Android™
Otto
EventBus Comparison with Square's Otto
eventbus-for-android