100行程式碼拆解EventBus核心邏輯(一)
- 2019 年 11 月 23 日
- 筆記
Github:https://github.com/hylinux1024 微信公眾號:終身開發者(angrycode)
EventBus 作為一個基礎的消息傳遞組件,了解其核心實現原理是日常開發工作之外需要做的必修課。本系列希望通過自己實現一個類似的消息傳遞組件 EasyBus 來理解 EventBus 的核心實現原理。

從官方的原理圖可以直觀的看出 EventBus 是一個基於訂閱發布的消息傳遞組件。訂閱者通過 EventBus 進行註冊,登記自己感興趣的事件 Event,發布者將 Event 發送到 EventBus 後就將會查詢監聽事件 Event 的訂閱者,然後將事件 Event 轉發到訂閱者中。
可以發現其實就是觀察者模式的實現。

觀察者模式中,被觀察者( Subject)內部會通過 List 來存儲觀察者的實例,然後就通過 notify() 方法可以通知觀察者。 在 EventBus 庫中,定義了 onEventXXXX(MessageEvent) 方法的類就是觀察者,而 EventBus 自己就是被觀察者。
接下來實現一個簡易版本的消息傳遞 EasyBus 。
首先預覽下整體的類結構
com/github/easybus├── EasyBus.java├── Logger.java├── SubscriberMethod.java├── Subscription.java└── demo ├── MainActivity.kt └── MessageEvent.java
參考 EventBus 定義了一個 EasyBus 類,這個將是我們使用 EasyBus 的主要入口。
單例方法
public class EasyBus { ... private EasyBus() { eventTypeBySubscriber = new HashMap<>(); subscriptionsByEventType = new HashMap<>(); } /** * 使用靜態內部類的方式實現單例 */ private static class Holder { static EasyBus instance = new EasyBus(); } public static EasyBus getInstance() { return Holder.instance; } ...}在 EasyBus 中使用靜態內部類的方式實現單例,這樣可以保證在一個進程內只有一個 EasyBus 的實例。 這裡有兩個變數需要重點解釋一下:
/** * 通過 EventType 查詢訂閱者資訊(即註冊EasyBus的Class類以及回調的通知方法) * 當調用post方法的時候,可以通過Event類查詢到對應的訂閱者資訊 * 從而快速的執行通知介面 */ private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; /** * 通過訂閱者(即註冊 EasyBus 的類實例)查詢事件 EventType Class * 事件類型即為 onEvent 方法中的參數類型 */ private final Map<Object, List<Class<?>>> eventTypeBySubscriber;
這兩個變數都是 Map 類型。subscriptionsByEventType 的 key 是 Event 類的類型, value 則訂閱者的列表資訊,通過 Subscription 類來封裝;而 eventTypeBySubscriber 的 key 是訂閱者實例, value 是 Event 類的類型。
回憶上面的觀察者模式類圖,被觀察者需要有一個 List 用來存儲觀察者列表,這裡的觀察者列表就是通過上面兩個 Map 變數來實現的。
register
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); Method[] methods = subscriberClass.getDeclaredMethods(); List<SubscriberMethod> subscriberMethods = new ArrayList<>(); for (Method method : methods) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length != 1) { continue; } if (method.getName().startsWith("onEvent")) { subscriberMethods.add(new SubscriberMethod(method, parameterTypes[0])); } } synchronized (this) { for (SubscriberMethod method : subscriberMethods) { subscribe(subscriber, method); } } }註冊方法的作用是解析觀察者的方法和事件類型,然後將觀察者資訊添加到 被觀察者中的 List 里。實現邏輯也很簡單,通過 Class 類進行反射調用類中的方法,過濾參數個數為1,且方法名稱是以 onEvent* 開頭的方法,就把其添加到 subscriberMethods 列表中。其中 SubscriberMethod 是訂閱者的接收通知的方法(可以對比觀察者模式中的 update 方法),它封裝了方法名稱、和方法中的參數類型(即事件類型)。
接下來就調用 subscribe 方法實現訂閱邏輯。
private void subscribe(Object subscriber, SubscriberMethod method) { Logger.i(subscriber.getClass().getSimpleName() + ":" + method.method.getName()); // 查詢這個事件類型中對應的訂閱者資訊(即接收相同事件的註冊者的方法有哪些) CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(method.eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); } subscriptions.add(new Subscription(subscriber, method)); subscriptionsByEventType.put(method.eventType, subscriptions); // 查詢註冊者中的接收的事件 Event 類型(即 onEvent 方法中的參數類型) List<Class<?>> eventTypes = eventTypeBySubscriber.get(subscriber); if (eventTypes == null) { = new CopyOnWriteArrayList<>(); } eventTypes.add(method.eventType); eventTypeBySubscriber.put(subscriber, eventTypes); }該方法的主要作用就是將上面解析到的方法和事件類型添加到 subscriptionsByEventType 和 eventTypeBySubscriber 這兩個 Map 中。 首先查詢 subscriptionsByEventType 是否有對應的訂閱資訊,如果沒有則初始化列表 subscriptions 。然後將訂閱者實例( subscriber)與訂閱者中的方法( method)封裝成 Subscription 。最後添加到 subscriptions 中。 同樣地查詢 eventTypeBySubscriber 中是否有對應的事件類型,沒有則初始化列表 eventTypes。之後將 eventTypes 存儲在 eventTypeBySubscriber 這個 Map 里。
post
public void post(Object event) { List<Subscription> subscriptionList = subscriptionsByEventType.get(event.getClass()); if (subscriptionList == null) { Logger.i("not found subscriber"); return; } for (Subscription subscription : subscriptionList) { subscription.notifySubscriber(event); } }post 邏輯也很簡單,主要通過事件類型查詢對應的訂閱者資訊 List ,然後遍歷該列表進行消息事件通知。
訂閱者資訊類主要封裝訂閱者實例資訊和對應的方法資訊
final class Subscription { final Object subscriber; final SubscriberMethod subscriberMethod; public Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = subscriber; this.subscriberMethod = subscriberMethod; } public void notifySubscriber(Object event) { try { subscriberMethod.method.invoke(subscriber, event); } catch (Exception e) { e.printStackTrace(); } } ...}Subscription 中還有一個重要的方法 notifySubscriber , 它是通過反射對訂閱者的方法進行調用。
unregister
梳理了上面的 register 方法 unregister 方法就比較容易理解了。
public void unregister(Object subscriber) { List<Class<?>> eventTypes = eventTypeBySubscriber.get(subscriber); if (eventTypes != null) { for (Class<?> eventType : eventTypes) { unsubscribe(subscriber, eventType); } } }通過訂閱者實例資訊從 eventTypeBySubscriber 查詢到訂閱的事件類型列表,然後遍歷 eventTypes 執行 unsubscribe 方法。
private void unsubscribe(Object subscriber, Class<?> eventType) { List<Subscription> subscriptionList = subscriptionsByEventType.get(eventType); if (subscriptionList == null) { return; } int size = subscriptionList.size(); for (int i = 0; i < size; i++) { if (subscriptionList.get(i).subscriber == subscriber) { subscriptionList.remove(i); i--; size--; } } }在 unsubscribe 方法中將訂閱者資訊從訂閱者列表中移除,就完成了退訂功能。
引用
- https://github.com/greenrobot/EventBus
- https://github.com/hylinux1024/EasyBus
