2018.2.11
我也想写一篇关于Eventbus的源码解读:今天动手了,真的是去了解它的内部实现.
EventBus
是greenrobot出品的一个用于Android中事件发布/订阅的库。以前传递对象可能通过接口、广播、文件等等,尤其像同一个
Activity
两个
Fragment
之间采用接口传递对象,十分的麻烦,而且耦合度较高。使用
EventBus
之后,这些将不再是问题。盗用GiHub上EventBus的一张图。
使用它有三个步骤,上面也写的非常清晰了.
Define events:
public
static
class
MessageEvent
{
/* Additional fields if needed */
}
Prepare subscribers: Declare and annotate your subscribing method, optionally specify a
thread mode
:
@Subscribe
(
threadMode
=
ThreadMode
.
MAIN
)
public
void
onMessageEvent(MessageEvent event) {
/* Do something */
};
还有Register and unregister your subscriber.
EventBus
.
getDefault()
.
register(
this
);
EventBus
.
getDefault()
.
unregister(
this
);
Post events:
EventBus
.
getDefault()
.
post(
new
MessageEvent());
线程模型是什么,首先就是
ThreadMode 类.
public enum ThreadMode {
POSTING,MAIN,MAIN_ORDERED,BACKGROUND,ASYNC
}
你需要指定它是属于哪类线程的.通常我们接收是在Main线程.@
Subscribe来注解,是3.0的新方式,不再用旧的onEventxxx()方法了.
public
@
interface
Subscribe,这是一个注解类.所以你需要了解注解是什么东西.
然后是
public static
EventBus getDefault(){} 这是单例的实现.
接着是注册与反注册:
public void
register(Object subscriber)它传入的对象是Object,就是任何对象都可以注册了.
反注册
public synchronized void
unregister(Object subscriber)
注册的通过SubscriberMethodFinder类查找:
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder
.findSubscriberMethods(subscriberClass);
synchronized
(
this
) {
for
(SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
显然是在SubscriberMethodFinder里面存在当前注册的对象了.
private static final
Map<Class<?>, List<SubscriberMethod>>
METHOD_CACHE
=
new
ConcurrentHashMap<>();这是存储类注册的map.
findSubscriberMethods()方法就是先查这个map,没有缓存就再查找:
if
(
ignoreGeneratedIndex
) {
subscriberMethods = findUsingReflection(subscriberClass);
}
else
{
subscriberMethods = findUsingInfo(subscriberClass);
}
这两个查找方法都最后通过findUsingReflectionInSingleClass来处理的.
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);
}
先查找方法,查完一轮就findState.moveToSuperclass();转到父类.直到完成.
findUsingReflectionInSingleClass找到所有的方法是非私有的,不是编译器生成的方法.
findState.findState.checkAdd(method, eventType)判断是否可以添加
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
}
这里的注释非常清楚了,先判断事件类型,这个比较快,如果不存在,就要判断完整的签名.毕竟有很多重载的方法.
查找到这差不多了,然后释放资源
getMethodsAndRelease(findState);
另一路查找
if
(findState.
subscriberInfo
!=
null
) {}这个大概是编译器annotationProcessor会产生一些信息.如果编译期就可以确定了,显然要比反射快一些.(?本人未证明是如此,只是猜测)
查找的结果是这个对象:
class
SubscriberMethod {
final
Method
method
; 作用的方法
final
ThreadMode
threadMode
; 线程
final
Class<?>
eventType
; 事件
final int
priority
; 优先级
final boolean
sticky
; 是否粘性
/** Used for efficient comparison */
String
methodString
; 只用于比较
}
查找完就注册了:注册就是对观察者处理.将观察者添加到一个列表中,将事件也添加到一个列表中.以便后面的发送事件使用.
// Must be called in synchronized block
private void
subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.
eventType
;
Subscription newSubscription =
new
Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions =
subscriptionsByEventType
.get(eventType);
//为每一个事件,提供一个CopyOnWriteArrayList来存储,这种列表,多线程读写性能好.读不加锁.
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);
}
}
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
;
}
}
List<Class<?>> subscribedEvents =
typesBySubscriber
.get(subscriber);
if
(subscribedEvents ==
null
) {
subscribedEvents =
new
ArrayList<>();
typesBySubscriber
.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//这里一个订阅者,对应一些事件列表,一个事件,可能对应多个订阅者.经过上面两步,就都完成了.通过不断地注册,系统中可能存在很多注册者.这些事件还按优先级顺序发送.
//如果接收sticky事件,立即分发sticky事件
if
(subscriberMethod.
sticky
) {
...
Object stickyEvent =
stickyEvents
.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
....
}
}
//事件所有订阅列表
private final
Map<Class<?>, CopyOnWriteArrayList<Subscription>>
subscriptionsByEventType
;
//一个订阅者的所有事件集
private final
Map<Object, List<Class<?>>>
typesBySubscriber
;
通过上面的流程可以看出,如果你注册了,但没有反注册,系统中的这些事件,与订阅者会越来越多,内存占用也越多,会导致内存泄露.但如果不断地注册与反注册,同样会有这些重复操作.如果在onstart里注册,这种显然不是太适合了.
发送事件:
public void
post(Object event) {
PostingThreadState postingState =
currentPostingThreadState
.get();
//ThreadLocal<PostingThreadState>
currentPostingThreadState,这是一个
ThreadLocal对象,找到当前的线程关联的状态.
//然后根据是否在发送状态处理.
List<Object> eventQueue = postingState.
eventQueue
;
eventQueue.add(event);
if
(!postingState.
isPosting
) {
postingState.
isMainThread
= isMainThread();
postingState.
isPosting
=
true
;
if
(postingState.
canceled
) {
throw new
EventBusException(
"Internal error. Abort state was not reset"
);
}
//未发送状态,就开始发送,一个一个事件.发送的线程是指定线程.
try
{
while
(!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(
0
), postingState);
}
}
finally
{
postingState.
isPosting
=
false
;
postingState.
isMainThread
=
false
;
}
}
}
//发送单个事件
private void
postSingleEvent(Object event, PostingThreadState postingState)
throws
Error {
Class<?> eventClass = event.getClass();
boolean
subscriptionFound =
false
;
if
(
eventInheritance
) { //处理继承的事件
List<Class<?>> eventTypes =
lookupAllEventTypes
(eventClass);
int
countTypes = eventTypes.size();
for
(
int
h =
0
; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
}
else
{
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
...订阅异常了.没找到结果处理
if
(!subscriptionFound) {
//
...
if
(
sendNoSubscriberEvent
&& eventClass != NoSubscriberEvent.
class
&&
eventClass != SubscriberExceptionEvent.
class
) {
post(
new
NoSubscriberEvent(
this
, event));
}
}
}
private boolean
postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized
(
this
) {
subscriptions =
subscriptionsByEventType
.get(eventClass);
}
找到所有的订阅源.然后发送
for
(Subscription subscription : subscriptions) {
postingState.
event
= event;
postingState.
subscription
= subscription;
boolean
aborted =
false
;
try
{
postToSubscription(subscription, event, postingState.
isMainThread
);
aborted = postingState.
canceled
;
}
......
}
最后线程分发
private void
postToSubscription(Subscription subscription, Object event,
boolean
isMainThread) {
switch
(subscription.
subscriberMethod
.
threadMode
) {
case
POSTING
:
invokeSubscriber(subscription, event);
break
;
case
MAIN
:
if
(isMainThread) {
invokeSubscriber(subscription, event);
}
else
{
mainThreadPoster
.enqueue(subscription, event);
}
break
;
case
MAIN_ORDERED
:
if
(
mainThreadPoster
!=
null
) {
mainThreadPoster
.enqueue(subscription, event);
}
else
{
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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
);
}
}
这个方法,根据注解,用不同的线程去发送事件.没什么要解释的.在添加注解时,就决定了它处于什么线程了,
MAIN在android里比较关注的主线程.它的实现是
HandlerPoster.看到Handler就知道,它是最终发送到主线程队列中.
到这,分发的流程就结束了:
EventBus#Post()
也只做了三件事
1. 根据订阅事件在
subscriptionsByEventType
中查找相应的订阅者
2. 分发订阅者的订阅事件调用线程
2. 通过反射调用订阅者的订阅事件
典型的观察者模式的应用.
这里需要的知识是ThreadLocal,它用于获取当前的线程.
ExecutorService线程池.在BackgroundPoster与AsyncPoster中用它发送事件.
其实它已经解决了一个问题,发送一个事件,可以到指定的线程中去做事,这其中的线程切换是我们需要关注的.这当然只是固定的模式,订阅线程是在开始就固定了.不像rxjava一样线程切换那么复杂.
反注册也可以理解了.就是把上面查找的两个列表清空了.
typesBySubscriber与subscriptionsByEventType里面相关的数据清除.
一般分析到这里就行了,但我并不想这样,因为里面还有一些比较重要的概念.流程,原理差不多就是上面的部分了.
注册(查找事件并添加),发送(查找对象,逐个发送),取消(查找事件并移除)
Poster各种发送器都有什么呢?它是一个接口,只有一个方法
void
enqueue(Subscription subscription, Object event);
这里的发送器有PendingPostQueue这个对象,它包含了要发送的事件列表.这个列表又包含了PendingPost pendingPost = PendingPost.
obtainPendingPost
(subscription, event);
有没有相似的感觉.这和系统的handler,message类似的.
非主线程是:
eventBus
.getExecutorService().execute(
this
);运行在线程池中.
主线程则是在handleMessage(Message msg) {}中处理的.
最后就是如何构建一个总线了
EventBusBuilder,建造者模式.
EventBus(EventBusBuilder builder) {
logger
= builder.getLogger();
subscriptionsByEventType
=
new
HashMap<>();
typesBySubscriber
=
new
HashMap<>();
stickyEvents
=
new
ConcurrentHashMap<>();
mainThreadSupport
= builder.getMainThreadSupport();
mainThreadPoster
=
mainThreadSupport
!=
null
?
mainThreadSupport
.createPoster(
this
) :
null
;
backgroundPoster
=
new
BackgroundPoster(
this
);
asyncPoster
=
new
AsyncPoster(
this
);
indexCount
= builder.
subscriberInfoIndexes
!=
null
? builder.
subscriberInfoIndexes
.size() :
0
;
subscriberMethodFinder
=
new
SubscriberMethodFinder(builder.
subscriberInfoIndexes
,
builder.
strictMethodVerification
, builder.
ignoreGeneratedIndex
);
logSubscriberExceptions
= builder.
logSubscriberExceptions
;
logNoSubscriberMessages
= builder.
logNoSubscriberMessages
;
sendSubscriberExceptionEvent
= builder.
sendSubscriberExceptionEvent
;
sendNoSubscriberEvent
= builder.
sendNoSubscriberEvent
;
throwSubscriberException
= builder.
throwSubscriberException
;
eventInheritance
= builder.
eventInheritance
;
executorService
= builder.
executorService
;
}
AsyncPoster与BackgroundPoster主要区别在于,前者是一次性的,后者是不断循环.如果执行失败了,还可以下次执行.像在后台的感觉.
参考: