EventBus是針對Android優化的發佈-訂閱事件總線,簡化了Android組件間的通訊。EventBus以其簡單易懂、優雅、開銷小等優勢而備受歡迎。java
github 地址:https://github.com/greenrobot/EventBusgit
api 'org.greenrobot:eventbus:3.0.0'
定義一個類做爲事件,能夠在類中定義不一樣的參數,發佈者賦值,訂閱者取值。
public class TestEvent { private String mName; public TestEvent(String name) { mName = name; } public String getEventName() { return mName; } }
首先須要將當前對象(Activity/Fragment等)與EventBus綁定(通常在onCreate函數中進行註冊)github
EventBus.getDefault().register(this);
接收事件的函數:設計模式
@Subscribe (threadMode = ThreadMode.MAIN, sticky = true) public void onTestKeyEvent(TestEvent event) { Log.d(TAG, "onTestKeyEvent | eventName=" + event.getEventName()); Toast.makeText(this, "test event, name=" + event.getEventName(), Toast.LENGTH_SHORT).show(); }
這裏經過註解的方式,定義事件的類型,和回調的線程等信息。api
查看EventBus jar包中Subscribe定義:緩存
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe {ThreadMode
threadMode() default ThreadMode.POSTING; /** * If true, delivers the most recent sticky event (posted with * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). */ booleansticky
() default false; /** Subscriber priority to influence the order of event delivery. * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of * delivery among subscribers with different {@link ThreadMode}s! */ intpriority
() default 0;
}
查看EventBus jar包中ThreadMode定義:安全
a) POSTING : 回調在發佈者線程併發
b) MAIN : 回調在主線程異步
c) BACKGROUND : 回調在子線程(若是發佈在子線程者回調直接運行在該線程)async
d) ASYNC : 異步回調(不回回調在發佈線程也不會回調在主線程)
發佈者不須要進行註冊,只須要將事件post出去。
a) 普通事件:EventBus.getDefault().post(new TestEvent("normalEvent"));
b) 粘性事件:EventBus.getDefault().postSticky(new TestEvent("stickEvent"));
普通事件和粘性事件區別:
若是發佈的是普通事件,當前若是沒有Subscriber,則後續註冊的Subscriber也不會收到該事件。
若是發佈的是粘性事件,當前若是沒有Subscriber,內部會暫存該事件,當註冊Subscriber時,該Subscriber會馬上收到該事件。
採用了典型的訂閱發佈設計模式。
// 這裏只分析其原理和結構不會細細推敲每一行代碼
訂閱者信息封裝(Subscription):
定義了兩個成員變量,
final Object subscriber; // 訂閱一個事件的對象
final SubscriberMethod subscriberMethod; // 訂閱的具體信息(方法名/ThreadMode/isStrick/priority)
EventBus主要成員變量:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; private final Map<Object, List<Class<?>>> typesBySubscriber; private final Map<Class<?>, Object> stickyEvents;
subscriptionsByEventType:以event(即事件類)爲key,以訂閱列表(Subscription)爲value,事件發送以後,在這裏尋找訂閱者,而Subscription又是一個CopyOnWriteArrayList,這是一個線程安全的容器。大家看會好奇,Subscription究竟是什麼,其實看下去就會了解的,如今先提早說下:Subscription是一個封裝類,封裝了訂閱者、訂閱方法這兩個類。
typesBySubscriber:以訂閱者類爲key,以event事件類爲value,在進行register或unregister操做的時候,會操做這個map。
stickyEvents:保存的是粘性事件
註冊過程,也就是調用regester函數的執行過程(主要是經過反射將註冊者信息添加到上述講的兩個map中:typesBySubscriber、subscriptionsByEventType)
a) SubscriberMethodFinder 是專門用來查找目標對象中全部訂閱函數(帶緩存,避免同一個類屢次反射查找)。反射能夠獲取函數的註解內容及每一個函數的返回值/修飾符,具體查看findUsingReflectionInSingleClass函數。
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } for (Method method : methods) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } }
b) 將訂閱函數添加到兩個緩存map中
c) 若是訂閱函數接收的是粘性事件,則將緩存中的粘性事件回調給該訂閱函數。
上述b) c) 兩個步驟的具體代碼以下:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType; Subscription newSubscription = new Subscription(subscriber, subscriberMethod); CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); if (subscriberMethod.sticky) { if (eventInheritance) { // Existing sticky events of all subclasses of eventType have to be considered. // Note: Iterating over all events may be inefficient with lots of sticky events, // thus data structure should be changed to allow a more efficient lookup // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
分發過程是從subscriptionsByEventType中取Subscriber並在指定的線程中回調接收函數的過程。
如何實如今不一樣線程中執行回調函數?
a)從訂閱信息中獲取訂閱函數回調線程。
b) 在指定線程中回調訂閱函數。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case POSTING: invokeSubscriber(subscription, event); break; case MAIN: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BACKGROUND: 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); } }
不一樣的的消息分發器在EventBus構造的時候初始化,下面看一下AsyncPoster的源碼以下:
class AsyncPoster implements Runnable { private final PendingPostQueue queue; private final EventBus eventBus; AsyncPoster(EventBus eventBus) { this.eventBus = eventBus; queue = new PendingPostQueue(); } public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); queue.enqueue(pendingPost); eventBus.getExecutorService().execute(this); } @Override public void run() { PendingPost pendingPost = queue.poll(); if(pendingPost == null) { throw new IllegalStateException("No pending post available"); } eventBus.invokeSubscriber(pendingPost); } }
AsyncPoster分發器繼承自Runable,核心是經過自定義的阻塞隊列維護消息,而後在EventBus定義的線程池中執行runable接口中的代碼。
EventBus中還定義了BackgroundPoster/HandlerPoster這裏不贅述。
其它細節:
上述分析只是講解了EventBus大概原理,並無細細分析。如,代碼中不少考慮了併發,事件優先級等