一、两个主要方法register和post。
register方法参数是当前类,找到当前类所有订阅的方法(@Subscribe注解、public声明、参数个数为1、非abstract、static)。如果没有用事先添加索引,这一步是需要用到反射的。索引是编译期间构建一个类,里面是一个静态的HashMap,key是订阅过的类,value是该类的SubscriberInfod对象,包含所有订阅的方法,ThreadMode,事件类型EventType等。
(如何添加索引 https://blog.csdn.net/z609933542/article/details/50953166)
找到所有订阅的方法后,挨个执行subscribe方法。核心是两个HashMap:
1.subscriptionsByEventType, key是事件类型eventType,value是Subscription对象(包含订阅类和订阅了该事件的类的方法)
2.typesBySubscriber,key是订阅类,value是该类订阅的所有事件集。
第一个HashMap用来通过事件寻找订阅者并分发事件;
第二个HashMap用来判断当前类是否已经注册过和取消注册。
执行subscribe方法的过程就是往这两个HashMap添加Key-Value的过程。
post方法参数是具体事件,过程简化了说就是通过subscriptionsByEventType查找所有订阅了改事件Subscription对象,挨个分发事件。
线程切换根据threadMode注解,通过各种Poster实现。
试想一下,如果你要手动撸一个EventBus,要怎么做?
每个类可能订阅一个或者几个事件,每个事件可能要发送给一个或者多个类。订阅的过程,就是通过类寻找订阅事件的过程,并建立 类->事件,事件->类两个映射HashMap;事件发送的过程就是通过具体事件寻找订阅者(也就是类)并挨个分发的过程。
==========================
二、关于反射
1.post找到订阅方法后分发给订阅者执行(invokeSubscriber)会用到反射(subscription.subscriberMethod.method.invoke(subscription.subscriber, event))
2.如果没有添加过索引,那么注册的过程中遍历类的方法会用到反射;
所以EventBus一定会用到反射,性能方面是有影响的。
==========================
三、关于跨线程
EventBusBuilder内部有个DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool()线程池,通过该线程池实现跨线程调度。
四、关于跨进程
EventBus不支持跨进程。跨进程可以参考升级版HermesEventBus。
EventBus主要用来模块/组件间解耦,本身不是为了跨进程设计的。想要用它来跨进程,就好比用杀鸡刀去杀牛,是不合适的。
在一个跨进程的场景下,如果你优先想到的方案是EventBus,那说明你的思路有问题。
五、优劣
优点:解耦,代码开发方便
缺点:需要定义大量的Event类、用到了反射、注解方法不能被混淆
六、内存泄漏
EventBus注册后如果没有取消注册,可能会内存泄漏。因为EventBus.register方法产生的中间对象会持有当前Class的实例。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
...
}
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
...
构造的Subscription对象的subscriber会指向注册时的Class对象。
网友评论