工欲善其事,必先利其器。要想非常好的将EventBus运用到项目的实践当中去,就必须能够对其框架的内部的机制有很好的理解。So,就有了下面的系列文章。
整个的EventBus框架分为两个包,22个类。包名分别是de.greenrobot.event与de.greenrobot.event.util。很显然,一个是框架的核心代码包,一个是其辅助作用的包,即工具类的集合。在de.greenrobot.event框架下一共有14个类,按照功能,可以大致分为这样几个方面:1)主进程与后台进程处理事件的真正工作者,包括:AsyncPoster、BackgroundPoster以及HandlerPoster。2)围绕整个框架的主题所涉及到的几个类,包括:EventBus、EventBusBuilder以及EventBusException。3)围绕订阅事件的对象Subscriber所需要的几个类,包括:NoSubscriberEvent、SubscriberExceptionEvent、SubscriberMethod以及SubscriberMethod。4)事件消息与事件消息队列相关的类,包括:PendingPost与PendingPostQueue。6)工具包不再一一说明。
1、详细讲解主进程与后台进程处理事件的真正工作者
AsyncPoster实际上是异步的处理事件消息的队列,在这个类中,所涉及到两个成员变量,分别是事件消息队列与主框架Bus。AsyncPoster实现Runnable的接口,其工作正如其Run方法中所体现的那样,从消息队列中取出消息,交给Bus进行触发。同时它还有一个将事件投放到事件队列中的功能。核心方法如下:
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
BackgroundPoster整体代码与AsyncPoster非常的类似,但是与其不同的是其run方法中是一个while的循环,在当前的事件队列不是空的情况下,会一直从事件的队列中获取事件并且交给Bus去触发。核心代码如下:
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;
}
HandlerPoster与上面两个同样是处理事件,但类型不同,HandlerPoster对应的是Handler。其余的功能上与BackgroundPoster保持一致。核心代码如下:
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、详细讲解围绕整个框架的主题所涉及到的几个类
EventBus无疑是一个核心,首先从构造函数来分析与猜测其大致的功能:1)初始化一个基于事件类型所分类的订阅对象的HashMap 2)通过订阅者所划分的类型的HashMap 3)初始化一个粘性事件所对应的一个ConcurrentHashMap 4)初始化三个任务的执行的对象 5)初始化搜索订阅者方法的对象。 6)相关的日志与异常所对应的布尔值的初始化 7)初始化一个线程池。
其中我们需要关注的方法是subscribe ,也就是具体体现了一个事件是如何被订阅的,其核心代码如下
//订阅方法的事件类型
Class<?> eventType = subscriberMethod.eventType;
//通过类型获取订阅的列表
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//基于参数创建一个新的订阅的对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(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);
//强制方法必须是public的类型
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//综合优先级与相应的索引进行对应的排序
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//通过订阅者得到当前的类型的列表
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//将订阅方法中的事件类型添加进去
subscribedEvents.add(eventType);
if (sticky) {
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());
}
}
还需要关心的是一个方法是如何被触发的,其方法是invokeSubscriber,对应的核心代码如下:
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);
}
本质上是利用反射机制 Method与invoke
EventBusBuilder可以说是EventBus的配置项,具体的配置属性可以从其成员变量中看出 1、创建默认线程池 2、相关日志打印的开关 3、部分Class 允许跳过检测
3、详细讲解围绕订阅事件的对象Subscriber所需要的几个类
SubscriberExceptionEvent正如其名称一样,它是由EventBus所提交的一个事件,当异常出现在一个订阅者的事件的处理中。
SubscriberMethod对应的是触发订阅者的方法,同样从其构造方法来猜测其大致的功能,1)成员变量Method的赋值 2)当前的事件在哪种线程的模式先工作,线程模式ThreadMode具体可以看见代码的注释。 3)触发的事件的类型
SubscriberMethodFinder的功能是寻找订阅者的哪些方法会被触发,还是用到了较多的反射机制,核心代码如下:
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
//是否是方法名以onEvent开头
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
//强制性是public的类型 并且不允许在忽略的类型的方法的里面
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//去除其中的参数
//通过函数名 决定在哪种线程中进行运行
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
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));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass();
Subscription是EventBus中的重要的数据模型,其维护的变量主要是包括:订阅者、订阅者的触发的方法以及触发的优先级。
OK,相信介绍到这里相信童鞋们对EventBus这种框架的代码以及有一定的了解了吧!希望这篇文章对大家有帮助,这样也不辜负我的文字啦~