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
变量来实现的。
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
里。
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
, 它是通过反射对订阅者的方法进行调用。
梳理了上面的 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
方法中将订阅者信息从订阅者列表中移除,就完成了退订功能。