EvenBus 是一款发布与订阅Android框架,和Java的RabbitMq类似。可以把EvenBus理解成是进程内的通信,RabbitMq可以是进程间和进程内的通信。
话不多说,这篇文章主要用于讲解大致流程和EvenBus的数据结构,细节在下一篇文章中会详细介绍。
一般而言,使用EvenBus的使用有如下流程
①:在需要的地方注册EvenBus
②:在注册过的对象里,写上一个
非抽象的
公用的
只带有一个参数的
带有@Subscribe注解的方法。
这篇文章,不对上述四个限制做代码分析,细节篇里会提到。
③:在发布数据的地方,调用EvenBus的post方法
这篇文章将针对上述三个流程做一个大致的分析。
一、注册EvenBus时,到底做了什么?
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscriber对象是我们注册进来的对象,第一步会得到我们注册进来的对象的类,然后用过EvenBus内部的解析器,把当前类的信息解析成EvenBus内部的数据结构 SubscriberMethod,我们进去看看这个类包含了哪些信息。
public class SubscriberMethod {
final Method method;//方法名称
final ThreadMode threadMode;
final Class<?> eventType;//方法里的参数类名
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
...... 构造器数据赋值 省略
}
}
经过subscriberMethodFinder.findSubscriberMethods这个方法后,EvenBus会把类解析一个SubscriberMethod数组。我们来看看,SubscriberMethod 包含了哪些信息。
Method method; ------ 标志了Subscribe的方法
ThreadMode threadMode ------- 线程模式
Class<?> eventType ------- 方法的参数类型
int priority ------ 优先级
boolean sticky ----- 是否是粘性
String methodString ---- 可忽略一般用于对比SubscriberMethod与SubscriberMethod时用到
上面描述可以很清晰的看到,当我们在写EvenBus的接收方法时配置的一些参数,例如配置对应的线程(ThreadMode)、优先级(priority )
继续往下看,当解析完调用 subscribe方法,传入注册的类以及解析完的SubscriberMethod。
来看下subscribe方法。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//把subscriber SubscriberMethod 封装成Subscription
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType是核心数据结构之一
//key参数类,Value是所有订阅了这个参数Subscription (简单理解成方法也没有问题)
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);
}
}
//插入数据 并且处理优先级
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);
if (subscriberMethod.sticky) {
........ 处理粘性事件
}
}
subscribe方法主要做了如下几步操作
第一:将注册的类与SubscriberMethod封装成一个Subscription
第二:将Subscription加入到subscriptionsByEventType这个Map里
第三:处理一个类内多个方法接收相同的参数,主要是typesBySubscriber这个Map
第四:处理粘性事件
这里主要是subscriptionsByEventType这个Map,这是一个哈希表。
到这里,EvenBus的注册这一块,已经分析完了。最后我们只要记住subscriptionsByEventType这个数据结构就可以了。key方法参数的类引用,Value是一个集合,里面存着当前Evenbus的所有订阅了当前参数的方法。
ok 下一步,我们来看看 EvenBus.Post时,发生了什么。
public void post(Object event) {
//处理线程问题
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//把当前事件添加到队列里面
eventQueue.add(event);
//如果当前线程的队列 是否正在post中
//这里这个处理我觉得一样不大
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//把当前队列的时间Post出去
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post方法主要做了这几件事
第一:获取线程队列
第二:循环这个队列,然后调用postSingleEvent。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//得到event的类引用以及所有父类引用
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 (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
这个方法分为是否需要考虑到父类和不需要考虑到父类。
然后调用了postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
.......
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
这个方法中 用到核心的数据结构 subscriptionsByEventType,从这个map中get到注册了当前参数key的所有方法。
然后循环 调用 postToSubscription(subscription, event,postingState.isMainThread);
postToSubscription方法很简单,区分线程类型,然后通过反射调用的注册的所有方法。
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);
}
}
线程不配置的话默认是POSTING 我们看invokeSubscriber方法
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
invokeSubscriber 通过反射调用了方法。
整体流程大致是这样~~~~