原理
一句话描述:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用
撇开专业术语,其实EventBus就是在内部【存储】了一堆onEvent开头的方法,然后post的时候,根据post传入的【参数】,去找到匹配的方法,【反射】调用之。
另外,它内部使用了【Map】进行存储,【键就是参数的Class类型】。知道是这个类型,那么你觉得根据post传入的参数进行查找还是个事么?
其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。
注册
简单说,EventBus.getDefault().register(this)就是去当前类,遍历所有的方法,找到onEvent开头的然后进行存储。
首先:EventBus.getDefault()其实就是个单例:
public
static EventBus getDefault
()
{
if
(defaultInstance ==
null)
{
synchronized
(EventBus.
class)
{
if
(defaultInstance ==
null)
{
defaultInstance =
new EventBus
();
}
}
}
return defaultInstance;
}
register公布给我们使用的有4个,本质上就调用了同一个:
public
void register
(Object subscriber
)
{ register
(subscriber, DEFAULT_METHOD_NAME,
false, 0
);
}
public
void register
(Object subscriber,
int priority
)
{ register
(subscriber, DEFAULT_METHOD_NAME,
false, priority
);
}
public
void registerSticky
(Object subscriber
)
{ register
(subscriber, DEFAULT_METHOD_NAME,
true, 0
);
}
public
void registerSticky
(Object subscriber,
int priority
)
{ register
(subscriber, DEFAULT_METHOD_NAME,
true, priority
);
}
private
synchronized
void register
(Object subscriber, String methodName,
boolean sticky,
int priority
)
{
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods
(subscriber.getClass
(), methodName
);
for
(SubscriberMethod subscriberMethod : subscriberMethods
)
{
subscribe
(subscriber, subscriberMethod, sticky, priority
);
}
}
四个参数
- subscriber 是我们扫描类的对象,也就是我们代码中常见的this;
- methodName 这个是写死的:"onEvent",用于确定扫描什么开头的方法,可见我们的类中都是以这个开头。
- sticky 这个参数,解释源码的时候解释
- priority 优先级,优先级越高,在调用的时候会越先调用。
1、首先会调用findSubscriberMethods方法
实际是去遍历该类内部所有方法,然后根据methodName去匹配,匹配成功的就封装成SubscriberMethod对象,最后存储到一个List并返回。
List<SubscriberMethod> findSubscriberMethods
(Class<?> subscriberClass, String eventMethodName
)
{
String key = subscriberClass.getName
() +
'.' + eventMethodName;
List<SubscriberMethod> subscriberMethods;
synchronized
(methodCache)
{
subscriberMethods = methodCache.get
(key
);
}
if
(subscriberMethods !=
null)
{
return subscriberMethods;
}
subscriberMethods =
new ArrayList<SubscriberMethod>
();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound =
new HashSet<String>
();
StringBuilder methodKeyBuilder =
new StringBuilder
();
while
(clazz !=
null)
{
String name = clazz.getName
();
if
(name.startsWith
("java.") || name.startsWith
("javax.") || name.startsWith
("android."))
{
// Skip system classes, this just degrades performance 降低性能
break;
}
// Starting with EventBus 2.2 we enforced 强制 methods to be public (might change with annotations again)
Method
[] methods = clazz.getMethods
();
//去得到所有的方法
for
(Method method : methods
)
{//开始遍历每一个方法,去匹配封装
String methodName = method.getName
();
if
(methodName.startsWith
(eventMethodName
))
{//分别判断了是否以onEvent开头,是否是public且非static和abstract方法,是否是一个参数。如果都符合,才进入封装的部分。
int modifiers = method.getModifiers
();
if
((modifiers & Modifier.PUBLIC
) != 0 &&
(modifiers & MODIFIERS_IGNORE
) == 0
)
{
Class<?>
[] parameterTypes = method.getParameterTypes
();
if
(parameterTypes.
length == 1
)
{
String modifierString = methodName.substring
(eventMethodName.length
());
ThreadMode threadMode;
if
(modifierString.length
() == 0
)
{//根据方法的后缀,来确定threadMode,threadMode是个枚举类型
threadMode = ThreadMode.PostThread;
}
else
if
(modifierString.equals
("MainThread"))
{
threadMode = ThreadMode.MainThread;
}
else
if
(modifierString.equals
("BackgroundThread"))
{
threadMode = ThreadMode.BackgroundThread;
}
else
if
(modifierString.equals
("Async"))
{
threadMode = ThreadMode.Async;
}
else
{
if
(skipMethodVerificationForClasses.containsKey
(clazz
))
{
continue;
}
else
{
throw
new EventBusException
("Illegal onEvent method, check for typos: " + method
);
}
}
Class<?> eventType = parameterTypes
[0
];
methodKeyBuilder.setLength
(0
);
methodKeyBuilder.append
(methodName
);
methodKeyBuilder.append
('>').append
(eventType.getName
());
String methodKey = methodKeyBuilder.toString
();
if
(eventTypesFound.add
(methodKey
))
{
// Only add if not already found in a sub class
subscriberMethods.add
(new SubscriberMethod
(method, threadMode, eventType
));
//将method, threadMode, eventType传入构造SubscriberMethod对象,添加到List
}
}
}
else
if
(!skipMethodVerificationForClasses.containsKey
(clazz
))
{
Log.d
(EventBus.TAG,
"Skipping method (not public, static or abstract): " + clazz +
"." + methodName
);
}
}
}
clazz = clazz.getSuperclass
();
//会扫描所有的父类,不仅仅是当前类
}
if
(subscriberMethods.isEmpty
())
{
throw
new EventBusException
("Subscriber " + subscriberClass +
" has no public methods called " + eventMethodName
);
//最常见的这个异常是在这里抛出来的
}
else
{
synchronized
(methodCache)
{
methodCache.put
(key, subscriberMethods
);
}
return subscriberMethods;
}
}
2、然后for循环扫描到的方法,然后调用suscribe方法
// Must be called in synchronized block
private
void subscribe
(Object subscriber, SubscriberMethod subscriberMethod,
boolean sticky,
int priority
)
{
subscribed =
true;
Class<?> eventType = subscriberMethod.eventType;
//
eventType
是我们方法参数的Class,
是
Map的
key
//根据subscriberMethod.eventType,去subscriptionsByEventType查找一个CopyOnWriteArrayList<Subscription> ,如果没有则创建。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get
(eventType
);
//
Map的value,
这里的subscriptionsByEventType是个Map,这个Map其实就是EventBus存储方法的地方
Subscription newSubscription =
new Subscription
(subscriber, subscriberMethod, priority
);
//
Map的value中保存的是
Subscription
if
(subscriptions ==
null)
{
subscriptions =
new CopyOnWriteArrayList<Subscription>
();
subscriptionsByEventType.put
(eventType, subscriptions
);
}
else
{
for
(Subscription subscription : subscriptions
)
{
if
(subscription.equals
(newSubscription
))
{
throw
new EventBusException
("Subscriber " + subscriber.getClass
() +
" already registered to event " + eventType
);
}
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);
int size = subscriptions.size
();
for
(int i = 0; i <= size; i++
)
{
//按照优先级添加newSubscription,优先级越高,会插到在当前List的前面。
if
(i == size || newSubscription.priority > subscriptions.get
(i
).priority
)
{
subscriptions.add
(i, newSubscription
);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get
(subscriber
);
//根据subscriber存储它所有的eventType
if
(subscribedEvents ==
null)
{
subscribedEvents =
new ArrayList<Class<?>>
();
typesBySubscriber.put
(subscriber, subscribedEvents
);
}
subscribedEvents.add
(eventType
);
if
(sticky
)
{
//判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行
Object stickyEvent;
synchronized
(stickyEvents)
{
stickyEvent = stickyEvents.get
(eventType
);
}
if
(stickyEvent !=
null)
{
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription
(newSubscription, stickyEvent, Looper.getMainLooper
() == Looper.myLooper
());
}
}
}
你只要记得一件事:扫描了所有的方法,把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList<Subscription> )中;
eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod, priority;包含了执行改方法所需的一切。
发布
调用很简单,一句话,你也可以叫发布,只要把这个param发布出去,EventBus会在它内部存储的方法中,进行扫描,找到参数匹配的,就使用反射进行调用。
/** Posts the given event to the event bus. */
public
void post
(Object event
)
{
PostingThreadState postingState = currentPostingThreadState.get
();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add
(event
);
if
(postingState.isPosting
)
{//防止每次post都会去调用整个队列
return;
}
else
{
postingState.isMainThread = Looper.getMainLooper
() == Looper.myLooper
();
//判断当前是否是UI线程(我去,竟然是通过这种方式判断的!)
postingState.isPosting =
true;
if
(postingState.canceled
)
{
throw
new EventBusException
("Internal error. Abort state was not reset");
}
try
{
while
(!eventQueue.isEmpty
())
{//遍历队列中的所有的event,调用postSingleEvent方法。
postSingleEvent
(eventQueue.remove
(0
), postingState
);
}
}
finally
{
postingState.isPosting =
false;
postingState.isMainThread =
false;
}
}
}
private
void postSingleEvent
(Object event, PostingThreadState postingState
)
throws Error
{
//得到event当前对象的Class,以及父类和接口的Class类型;主要用于匹配,比如你传入Dog extends Animal,他会把Animal也装到该List中。
Class<?
extends Object> eventClass = event.getClass
();
List<Class<?>> eventTypes = findEventTypes
(eventClass
);
boolean subscriptionFound =
false;
int countTypes = eventTypes.size
();
for
(int h = 0; h < countTypes; h++
)
{
//遍历所有的Class,到subscriptionsByEventType去查找subscriptions,register时我们就是把方法存在了这个Map里
Class<?> clazz = eventTypes.get
(h
);
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized
(this)
{
subscriptions = subscriptionsByEventType.get
(clazz
);
}
if
(subscriptions !=
null && !subscriptions.isEmpty
())
{
for
(Subscription subscription : subscriptions
)
{
postingState.event = event;
postingState.subscription = subscription;
boolean aborted =
false;
try
{
postToSubscription
(subscription, event, postingState.isMainThread
);
//反射执行方法
aborted = postingState.canceled;
}
finally
{
postingState.event =
null;
postingState.subscription =
null;
postingState.canceled =
false;
}
if
(aborted
)
{
break;
}
}
subscriptionFound =
true;
}
}
if
(!subscriptionFound
)
{
Log.d
(TAG,
"No subscribers registered for event " + eventClass
);
if
(eventClass != NoSubscriberEvent.
class && eventClass != SubscriberExceptionEvent.
class)
{
post
(new NoSubscriberEvent
(this, event
));
}
}
}
private
void postToSubscription
(Subscription subscription, Object event,
boolean isMainThread
)
{
switch
(subscription.subscriberMethod.threadMode
)
{
//根据threadMode去判断应该在哪个线程去执行该方法
case PostThread:
invokeSubscriber
(subscription, event
);
//直接【反射】调用;也就是说在当前的线程直接调用该方法
break;
case MainThread:
if
(isMainThread
)
{//如果是UI线程,则直接调用
invokeSubscriber
(subscription, event
);
}
else
{
//否则把当前的方法加入到队列,然后直接通过【handler】去发送一个消息,并在handler的handleMessage中去执行我们的方法
mainThreadPoster.enqueue
(subscription, event
);
}
break;
case BackgroundThread:
if
(isMainThread
)
{
//如果是UI线程,则将任务加入到后台的一个【队列】,最终由Eventbus中的一个【线程池】去调用executorService = Executors.newCachedThreadPool();
backgroundPoster.enqueue
(subscription, event
);
}
else
{
//如果当前非UI线程,则直接调用
invokeSubscriber
(subscription, event
);
}
break;
case Async:
//将任务加入到后台的一个【队列】,最终由Eventbus中的一个【线程池】去调用;线程池与BackgroundThread用的是【同一个】
asyncPoster.enqueue
(subscription, event
);
//BackgroundThread中的任务,【一个接着一个去调用】,中间使用了一个布尔型变量进行的控制。Async则会【动态控制并发】
break;
default:
throw
new IllegalStateException
("Unknown thread mode: " + subscription.subscriberMethod.threadMode
);
}
}
粘性事件
介绍了register和post;大家获取还能想到一个词sticky,在register中,如果sticky为true,会去stickyEvents去查找事件,然后立即去post;
那么这个stickyEvents何时进行保存事件呢?
其实evevntbus中,除了post发布事件,还有一个方法也可以:
public
void postSticky
(Object event
)
{
synchronized
(stickyEvents)
{
stickyEvents.put
(event.getClass
(), event
);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post
(event
);
}
和post功能类似,但是会把方法存储到stickyEvents中去;
大家再去看看EventBus中所有的public方法,无非都是一些状态判断,获取事件,移除事件的方法;没什么好介绍的,基本见名知意。
点关注,不迷路
文章每周持续更新,可以微信搜索「 十分钟学编程 」第一时间阅读和催更,如果这个文章写得还不错,觉得有点东西的话 ~求点赞👍 求关注❤️ 求分享❤️
各位的支持和认可,就是我创作的最大动力,我们下篇文章见!