EventBus源码解析
很久没有更新过源码解析类文章,以下内容作为源码分析类的笔记。分析方法适用于其它源码分析。
分析工具说明
许久以来,阅读源代码最得力的工具就非Source Insight莫属了。然,后来微软出了一款轻量级但功能强大的IDE就没Source Insight什么事了。微软的这款IDE就是大名鼎鼎的VSCODE,全名叫:Visual Studio Code。它的强大之处这里就不过多描述了。我们转回到正题。
获取源码并导入IDE
我们在Github.com上搜索到EventBus的项目地址,然后进行克隆即可。
git clone https://github.com/greenrobot/EventBus.git
克隆完成之后,导入IDE。打开VS CODE,在菜单栏中选择File -> Open,选择克隆后文件所在的目录,然后点击确认。这就完成了代码导入工作。
由于我们只是分析EventBus的骨架脉络,所以我们要选择EventBus的早期版本。这里我们将要选择V1.0.1版。这同样适用于Android源代码分析,适宜选择1.5版或1.6版。
切换版本的步骤为:先进入EventBus刚才的克隆目录,然后执行以下步骤:
//列出EventBus项目的所有tag。
sahadevs-MacBook-Pro:EventBus sahadev$ git tag
V1.0.1
V2.2.0
V2.3.0
V2.4.0
V3.0.0
//上面可以看出EventBus共发布了5个版本。我们切换到V1.0.1版。
sahadevs-MacBook-Pro:EventBus sahadev$ git checkout V1.0.1
经过以上步骤,我们的代码就切换到了V1.0.1版本。VS CODE中的内容也会同步更新。接下来开始我们的代码分析。
事件监听注册
我们熟知EventBus的用法,其中最有代表性的入口为:
EventBus.getDefault().register(this);
为了方便分析,上面这行代码放在Activity中执行。所以上面的this就是Activity的实例。EventBus采用了单例模式,在这个版本中,采用了最简便的实现方式:
public class EventBus {
private static final EventBus defaultInstance = new EventBus();
public static EventBus getDefault() {
return defaultInstance;
}
}
好,以上并不是我们的重点。我们要从register方法说起。
public void register(Object subscriber) {
register(subscriber, defaultMethodName, ThreadMode.PostThread);
}
public void register(Object subscriber, String methodName, ThreadMode threadMode) {
List subscriberMethods = findSubscriberMethods(subscriber.getClass(), methodName);
for (Method method : subscriberMethods) {
Class> eventType = method.getParameterTypes()[0];
subscribe(subscriber, method, eventType, threadMode);
}
}
register(this)方法执行时调用了register(Object subscriber, String methodName, ThreadMode threadMode),subscriber是Activity,methodName采用了defaultMethodName的值,这个值在EventBus中定义为:'onEvent',threadMode为ThreadMode.PostThread。其中ThreadMode为一个枚举,它有两个值:
public enum ThreadMode {
/** Subscriber will be called in the same thread, which is posting the event. */
PostThread,
/** Subscriber will be called in Android's main thread (sometimes referred to as UI thread). */
MainThread,
/* BackgroundThread */
}
PostThread用于标识发送线程,MainThread用于标识主线程。它们在后期执行的区别为:如果在注册时为PostThread,则事件发送后,会直接调起监听方法。而如果注册时为MainThread,则会经过主线程的消息队列调起。
好,回到刚才的地方,在register(Object subscriber, String methodName, ThreadMode threadMode)方法内先执行了findSubscriberMethods,我们移步看看这个方法内执行了什么工作:
private List findSubscriberMethods(Class> subscriberClass, String methodName) {
...
List subscriberMethods;
...
subscriberMethods = new ArrayList();
Class> clazz = subscriberClass;
HashSet> eventTypesFound = new HashSet>();
while (clazz != null) {
String name = clazz.getName();
...
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
if (eventTypesFound.add(parameterTypes[0])) {
// Only add if not already found in a sub class
subscriberMethods.add(method);
}
}
}
}
clazz = clazz.getSuperclass();
}
...
}
这里面的代码比较长,将不必要的略去。这段代码主要做了以下工作:
根据给定的类,遍历该类及其父类所有的方法,如果遍历时发现与给定的方法名相等,并且该方法的参数只有一个,那么就将该方法加入到subscriberMethods中,最后返回。这里被找到的方法是同名的,但是参数类型不同,这里的参数类型决定了将来要监听的事件类型。
好,我们回到findSubscriberMethods之前被调用的地方,继续往下走。接下来的动作是将findSubscriberMethods方法返回的Method列表进行遍历。而这次遍历时,将Method的第一个参数类型作为参数传给了subscribe方法:
private void subscribe(Object subscriber, Method subscriberMethod, Class> eventType, ThreadMode threadMode) {
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList();
subscriptionsByEventType.put(eventType, subscriptions);
}
...
subscriberMethod.setAccessible(true);
Subscription subscription = new Subscription(subscriber, subscriberMethod, threadMode);
subscriptions.add(subscription);
...
}
该方法首先做的就是根据eventType从subscriptionsByEventType对象中取出对应的subscriptions,然这里第一次肯定是null。所以先进行实例化,然后存放于subscriptionsByEventType中。而subscriptionsByEventType对象存放的就是根据eventType所对应的subscriptions。而subscriptions中存放的就是所有的观察者,待会有时间发生时,根据事件的类型,从subscriptionsByEventType中取出所有的观察者subscriptions逐一通知。
在创建了观察者列表subscriptions之后开始创建具体的观察者。Subscription是一个基本的类,它的示例只是持有了subscriber(Activity),subscriberMethod(onEvent),threadMode(ThreadMode.PostThread)三者的引用而已。
到这里,观察者Activity监听某一事件的注册工作就算完成了。
事件发送
接下来,我们将从事件发送入口post方法开始分析:
public void post(Object event) {
//这里的两个属性很重要,下面这个用于缓存事件,待下次事件执行时,一并执行。
List
这里最开始有两个EventBus的成员属性,这里很重要:currentThreadEventQueue与currentThreadIsPosting。这两个属性本身是和线程有关系的。关于这两个属性内部的作用机制这里就不再赘述了。上面post方法的第一行currentThreadEventQueue.get()返回的是一个空的ArrayList
通过判断,最终进入postSingleEvent方法内,而该方法则是事件分发的核心:
private void postSingleEvent(Object event) throws Error {
List> eventTypes = findEventTypes(event.getClass());
...
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class> clazz = eventTypes.get(h);
CopyOnWriteArrayList subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(clazz);
}
if (subscriptions != null) {
for (Subscription subscription : subscriptions) {
if (subscription.threadMode == ThreadMode.PostThread) {
postToSubscribtion(subscription, event);
} else if (subscription.threadMode == ThreadMode.MainThread) {
mainThreadPoster.enqueue(event, subscription);
} else {
throw new IllegalStateException("Unknown thread mode: " + subscription.threadMode);
}
}
...
}
}
...
}
这个方法内首先调用了findEventTypes方法,我们先移步看看它做了什么工作:
private List> findEventTypes(Class> eventClass) {
...
Class> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
return eventTypes;
}
这部分的主要工作为:将指定类以及父类所实现的接口及其本身的类型加入到eventTypes中,最后将eventTypes返回。如果我们在定义事件类型时,没有继承任何类、实现任何接口,那么就会省去这个步骤,节省时间。
回到postSingleEvent方法继续往下看:根据刚刚返回的事件类型集合开始遍历,每次遍历时,根据给定的事件类型从subscriptionsByEventType中寻找对应的监听者。我们在上面分析注册部分时候已经见过,subscriptionsByEventType中存放的就是事件类型与监听者的键值对。找到监听者列表subscriptions之后,开始对subscriptions进行遍历,进行消息下发。
消息的发送主要两种模式:
- PostThread
- MainThread
我们在开头的时候就提到过,PostThread会通过反射直接调起对应的方法,而MainThread则会通过主线程的消息队列进行调起。我们来看看究竟是不是这样:
//PostThread的执行方式
static void postToSubscribtion(Subscription subscription, Object event) throws Error {
...
subscription.method.invoke(subscription.subscriber, event);
...
}
//MainThread的执行方式
void enqueue(Object event, Subscription subscription) {
PendingPost pendingPost = PendingPost.obtainPendingPost(event, subscription);
Message message = obtainMessage();
message.obj = pendingPost;
if (!sendMessage(message)) {
throw new RuntimeException("Could not send handler message");
}
}
可以看到:对于PostThread类型,直接通过反射将事件传给了观察者。而对于MainThread类型,则是通过Handler将信息传给了主线程。最后消息到达主线程后,通过与PostThread方式一样经过postToSubscribtion方法执行。
以上就是关于EventBus事件监听注册、事件广播的解析过程。