引言: 最近看了EventBus的源码,看完后分享下自己的理解
- 写法约定: 文件的eventTypeClass 对应事件的Class对象,ObjectClass对应Object类的Class对象
- EventBus简介:以观察者模式为基本思想,实现消息推送/订阅功能,用于Android组件之间相互通信的开源框架。
1. EventBus类结构分析
EventBus可以说是一个比较简洁的事件订阅框架了,其实EventBus中就可以看到很多的实现代码了,所以EventBus的源码还是挺好理解的,阅读过程围绕几个关键流程展开就好。
//EventBus的映射表
public class EventBus {
//每一个事件对应的事件集合(如果EventBusBuilder.eventInheritance为true,他会收集当前事件类的所有接口和父类)
//如果是接口会向集成关系上层递归遍历所有的interface,如果是当前事件的父类则直向上收集一层,如果当前类的父类是Object了,那么ObjectClass也会被加入到List<Class>集合中,下面具体看这些映射表存储的是什么.
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
//事件和订阅者方法映射表,可以形成:eventTypeClass->subscriberObject, eventTypeClass, method,
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//<Subscriber, List<eventType>> subscriber->eventTypeClass*
private final Map<Object, List<Class<?>>> typesBySubscriber;
//粘性事件Class对象和粘性事件映射表
private final Map<Class<?>, Object> stickyEvents;
}
从上面EventBus的结构中就可以看出,EventBus管理事件的方法,就是用建立一些映射表,存储[事件-发送者-订阅者]的关联关系, 这也是观察者模式一种通用的思路,用第三方的集合或者映射表管理事件、发送者和订阅者的关系,可以实现结构上比较松散的耦合
另外,上面这些映射表的命名都很有规律,[aaa]sBy[bbb],那么aaa就是map中的value, bbb就是key,这种命名方式在日常开发中可以借鉴的。
2. 注册流程分析
- 2.1 注册:EventBus#register()
EventBus#register()的实现:
public void register(Object subscriber) {
//(1)使用注册时传入的Object,其实是想拿Class对象,有了一个对象的Class对象可以说就掌握某个interface,class的所有内容
Class<?> subscriberClass = subscriber.getClass();
//(2)使用订阅者Class对象查找进行订阅了的Method
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//(3)遍历注册时传入的订阅者的所有订阅方法,并将订阅者和订阅方法建立订阅关系
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
- SubscriberMethod数据结构
//从结构中可以看到其中封装了订阅者的Method,eventType等信息
public class SubscriberMethod {
final Method method; //进行注册的订阅者Class对象或者到的Method对象
final ThreadMode threadMode;
final Class<?> eventType; //发送-接收过程中定义的事件Class对象,后面从源码中验证其含义
final int priority; //优先级
final boolean sticky; //是否标记为粘性事件
/** Used for efficient comparison */
String methodString;
.....
}
- Subscription数据结构
/**
* 将订阅者和订阅方法封装在一起
*/
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;
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
//这个hashCode被重写了,拼接了订阅方法的签名字符串,要知道为啥这样做,就要看下SubscriberMethod#equals()的实现了
//这么做会把本来对比int值变成了比较String类型的值
//不用直接用Method的equals()方法,好像说是有性能问题,没看太清楚,后面了解清楚了再进行说明,先跟进今天的主题
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
- SubscriberMethodFinder#findSubscriberMethods()方法的实现:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//这个METHOD_CACHE从下面METHOD_CACHE.put(subscriberClass, subscriberMethods)就可以看出是存储订阅者和订阅方法的映射表的,
//比如MainActivity对应这public void onMessageEvent(MessageEvent)这个subscriberMethod
//他是先从缓存中拿,为null时才重新进行findXxx()操作
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//这个ignoreGeneratedIndex是记录是否用启动SubscriberInfoIndex机制,先明白这个SubscriberInfoIndex是用来加事件管理的缓存的就好了,具体的实现会在本文第6个部分解释
//ignoreGeneratedIndex默认为false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
- SubscriberMethodFinder#findUsingReflection():
//这个方法是从订阅的subscriberClass中查找使用了@Subscribe注解的方法,比如:EventBusTestActivity中的onMessagEvent(MessageEvent)方法
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//FindState中封装了订阅者的Class对象,SubscriberInfo对象
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
- 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();
//关键是这里传入的eventType,从上文中可以看出,是我们添加了@Subscribe注解方法的第一个参数,那多个参数不行吗?这个问题先留着
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");
}
}
}
- SubscriberMethodFinder#findUsingInfo()的实现:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
- 2.2 订阅: subscribe(subscriber, subscriberMethod)方法的实现
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//根据订阅者对象查找SubscriberMethod集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//针对每一个SubscriberMethod,这里遍历subscriber集合,其实是真正遍历是SubscriberMethod结构中事件(eventType)
//因为到了这一步,已经有了包含eventType, method的结构集合了,下一步应该建立订阅者和事件的映射关系了
//订阅是要将当前订阅者,所有的事件和对应的订阅者(订阅方法)建立映射关系,这样才能够将eventType对应多个订阅者的关系建立起来
subscribe(subscriber, subscriberMethod);
}
}
}
- subscribe()方法的实现:
//源码中subscribe()的实现代码其实糅杂了三个方面的子功能,我们进行下等价转换,换成下面这样,就很清楚了
/**
* 分别建立:事件和订阅者集合、订阅者和事件集合的映射关系
*/
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//(1)建立当前事件和订阅者一对多关系的映射表,Subscription中封装了订阅者,事件,订阅方法的集合
buildSubscriptionsByEventTypeMap(subscriber, subscriberMethod, eventType, newSubscription);
//(2)建立<subscriber, List<eventTypes>>映射表
buildTypesBySubscriberMap(subscriber, eventType);
//(3)如果是粘性事件,则在注册时需要进行发送的,这个是粘性事件的特点,不是在post()时进行发布事件,而是在register()的时候把事件发布出去
postStickyEventToSubsriptionIfNeed(subscriberMethod, eventType, newSubscription);
}
private void postStickyEventToSubsriptionIfNeed(SubscriberMethod subscriberMethod, Class<?> eventType, Subscription newSubscription) {
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);
}
}
}
private void buildTypesBySubscriberMap(Object subscriber, Class<?> eventType) {
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
}
private void buildSubscriptionsByEventTypeMap(Object subscriber, SubscriberMethod subscriberMethod, Class<?> eventType, Subscription newSubscription) {
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);
}
}
//加入subscriptionsByEventType
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;
}
}
}
问题1:subscriber和subscriberMethods为什么要建立订阅关系?
EventBus#post(MessageEvent) 怎么才能够知道发送给谁?
下面贴一个实例:
public class EventBusTestActivity extends AppCompatActivity {
static final String TAG = EventBusTestActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus_test);
}
@Override
protected void onStart() {
super.onStart();
//这个注册事件的观察者直接是当前类的this对象,很暴力直接
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
}
public void onClick(View view) {
EventBus.getDefault().post(new MessageEvent());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEventReceive(MessageEvent messageEvent) {
Log.d(TAG, "onMessageEventReceive: ");
}
}
3.post方法实现分析
//发送事件,这个发送事件的调用跟当前的上下文无关
EventBus.getDefault().post(new MessageEvent());
//注解声明的订阅方法接收事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Log.d(TAG, "onMessageEvent: ");
}
4.三种Poster和ThreadMode
- 从EventBus的成员变量和EventBus::postToSubscription()方法中可以看到,进行事件的发送是通过三类Poster进行的额,那么我们看看这三类Poster的实现上有什么区别。
- 先看下ThreadMode的定义:
public enum ThreadMode {
POSTING, //发送事件和执行订阅方法在同一个线程中,实现上是直接调用invokeSubscriber(),不切换线程也不进行队列阻塞
MAIN, //订阅方法在主线程中执行
BACKGROUND, //订阅方法在子线程中执行
ASYNC //订阅方法在子线程中执行,没有使用生产者消费者模式
}
- 1.HandlerPoster对应ThreadMode.MAIN
这个是直接继承自Handler实现了一个Handler的事件处理队列,从EventBus中mHandlerPoster创建实例传入的Looper实例可以知道,这个Handler是运行在主线程的。因为Handler的事件会被切换到主线程运行,所以使用这个类型的poster不用担心调用post的位置,都可以保证订阅方法最后运行在主线程。并且还能看出来这种做法的原型是生产-消费者模式。
EventBus(EventBusBuilder builder) {
...
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
...
}
final class HandlerPoster extends Handler {
...
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
...
}
- 2.BackgroundPoster对应ThreadMode.BACKGROUND
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
//运行在线程池中
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
跟进去之后可以发现,对于EventBus.getDefault()实例对应的,这个线程池的实现,当然EventBus是可以自己去构建一个EventBusBuilder实例的,DEFAULT_EXECUTOR_SERVICE 只是一个默认的构建器。这样就可以确定,这个poster发送出去的事件,订阅者方法都是运行在子线程中。
他是一个同步等待的消息处理队列。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
- 3.AysnPoster对应ThreadMode.ASYNC
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的实现和简单,抛弃了生产-消费者模式,直接将一个事件入队列,然后运行时从队列中取出一个事件的订阅方法执行,那么,这种方式就会是异步的,适合耗时的异步任务,不会造成后台线程阻。
5. 解注册做了什么?
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
6. EventBus现有的不足与完善思路
EventBus在使用起来很简单,可以说很大程度上是因为使用了反射进行了一系列内部的操作,但是我们都知道大量的反射是很损耗性能的,比如根据Class然后遍历其所有的method这种做法其实是比较低效的。EventBus3.0 之后提供了AnnnationProcessor机制
- 配置SubscriberInfoIndex 方式:
//build.gradle
dependencies {
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
android {
javaCompileOptions {
annotationProcessorOptions {
// EventBus Apt 索引类生成位置
arguments = [eventBusIndex: applicationId + '.MyEventBusIndex']
}
}
}
}
//使用索引类
EventBus.builder().ignoreGeneratedIndex(false) // 使用 Apt 插件 默认值其实也是false
.addIndex(new MyEventBusIndex()) // 添加索引类
.installDefaultEventBus();
.register(this); // 作为默认配置
- 会产生以下代码:
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(EventBusTestActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessageEventReceive", MessageEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onTeskRun", Runnable.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(EventBusHelper.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventBus", EventBusHelper.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
- 6.1 跨进程通信怎么办?
看了这里,我有个疑问,如果是要进行跨进程的消息发送,EventBus能实现吗?
通读整个EventBus的文档和源码,完全没看见跨进程通信的影子,因为要实现跨进程起码得有个全局广播,AIDL,或者Socket吧,但是都没有。再加上官方文档在说明EventBus用途时也并没有提到EvenBus支持了跨进程通信。但是,从另一方面考虑,随着app的功能越庞大,组件化等技术的应用,多进程应用已经是很平常的事情了,跨进程通信也是刚需了,所以这也是EventBus现有功能的缺陷。如果需要扩展EventBus进行跨进程通信,可以参考github上已有的项目,
7. 小结
EventBus实现几个关键思路:
- 设计模式:观察者模式、构建者模式、策略模式
- 进行源码分析的几个关键类:
- 提供对外接口的类: EventBus,
- 封装结构的类:EventBusBuilder, Subscription, SubscriberMethod
- 关键驱动型类:SubscriberMethodFinder 完成订阅者方法的收集器
- 事件发送者(Poster): HandlerPoster, BackgroundPoster, AsyncPoster,生产消费者模型的应用
- EventBus实现的主要思路
- 1.注册流程(关键是在建立订阅关系时建立一个映射表)
- (1) 注册
- (2) 建立订阅者和订阅方法的映射表,查找订阅者有多少需要订阅方法(Subscrib注解声明的方法)
- (3) 建立事件和订阅者的映射表,查找post出去的事件所有订阅者,然后把事件发出去
- 2.事件发送流程
- (1) 拿到缓存的事件集合
- (2) 遍历事件,找到每一个事件的订阅者,依次把事件post到对应的订阅者
- 1.注册流程(关键是在建立订阅关系时建立一个映射表)
4万+

被折叠的 条评论
为什么被折叠?



