EventBus其实解决了这样几个问题:保存订阅者、发布事件、切换线程、粘性事件
保存订阅者
在register(this)时,处理运行时注解,根据this找到类和类中的方法,根据方法的Subscribe注解找到订阅事件的那些方法。
从Subscribe注解中找到指定的线程、优先级、粘滞等;
从方法参数中找到订阅的事件类eventType;
生成订阅者Subscription对象,内有订阅的类和方法的引用
然后用eventType做key,把订阅者存入concurrentHashMap中,因为一个eventType可能有多个订阅者,而且是典型读多写少的场景,所以用CopyOnWriteArrayList来保存这些订阅者。
发布事件
在post时,根据eventType,从concurrentHashMap中寻找订阅者。
会根据父类查找,eventType是根据对象的类和父类一起判断的,所以会根据一个类的列表去查询订阅者。
会排队处理,为了确保一个接一个地发送事件,post其实是先进入队列Queue,然后每次从队列中弹一个event来处理的,这个队列是个普通的List,实际上作为FIFO处理。
切换线程
同一个事件的不同订阅者,可能需要不同的线程去执行。
在找到订阅者处理时,根据订阅者要求的线程模式,做个switch处理:
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);
}
可以看到,有默认、mainThreadPoster、backgroundPoster、asyncHandler四类。
默认会在当前线程直接invokeSubscriber,传递事件给订阅者。
mainThreadPoster其实就是一个持有主线程Looper的handler,利用handler消息机制,实现在主线程的handleMessage函数里调用eventbus的invokeSubscriber。
backgroundPoster和asyncPoster都是Runnable,都是获取eventbus中的ExecutorService来处理,不过
asyncPoster对于队列没做什么特殊处理,每个事件都会一个线程。
而backgroundPoster做了入队列的锁同步,在出队列处理时,还可以等待1000毫秒:
PendingPost pendingPost = queue.poll(1000);
其实就是说,backgroundPoster会尽量复用线程。
粘滞消息
一般事件是订阅在先,接收在后,但是有些情况下,事件已经发送,再去订阅时还希望能拿到这个事件,这时候就需要粘性事件。
粘性事件的注册也是在注解中:
@Subscribe(sticky = true)
而粘性事件的发送也有专门的函数:
EventBus.getDefault().postSticky(new MessageEvent("xxx"));
EventBus.getDefault().getStickyEvent(MessageEvent.class);
EventBus.getDefault().removeStickyEvent(stickyEvent);
粘性事件的实现原理,也是EventBus维持了一个concurrentHashMap类型的stickyEvents,这个Map的key是event.Class,value则是event本身。
粘性事件的发送有两个入口:
一是postSticky,每次都会先更新stickyEvents中的数据,然后post这个event。
二是register,相当于补发事件,实现粘滞效果,具体是在每次register时,会查询stickyEvent集合中,有没有粘性事件,如果有,就补发。
网友评论