EventBus1.0.1源码解析
很久没有更过文章了,今天有时间我想着写一篇文章来冒个泡,接下来的一段时间我会对一些开源框架的源码进行一些分析。我为什么要分析开源框架的源码呢?主要有几方面原因:
- 分析开源框架的源码使自己在这些开源框架中学到一些作者的思想。
- 阅读开源框架的源码对架构方面的知识也有一定的提高。
- 可以了解到自己有哪些api不熟悉或者不了解可以去查询学习这样会很好的提高自己。
- 可以开阔自己的思想,遇到问题的时候可能会多一点的解决方案
- 提高自己的代码阅读能力和编码能力有助于自己写出更好、质量更高的代码。
-
阅读一些框架的源码时,我的建议是不要究其细节,不要想着把每一行代码都弄清楚,我们只要知道它整体的实现原理和思想就ok了。如果你老是想着弄懂每一行代码什么意思的话你很容易就迷失进去不能自拔,最后导致你再也不想阅读好的框架的源码了,这样你肯能就少了很多学习的资源。
好了,说了这么多主要是说了一下阅读源码的一些好处以及阅读源码的建议。接下来我们说一下片文章的主题,这篇文章主要是分析一下EventBus1.0.1的源码,虽然现在EventBus现在已经升级到了3.1.1版本了,但是我想说的是它的基本思路是没有变化的。我们分析一个框架的时候可以选择一个比较纯净的版本,没有那么多扩展和修复的版本,这样我们就很容易分析到它的本质。
eventBut 整体实际上就是一个观察这模式,只要我们订阅了这个事件发布者有什么变化我们就会时时的得到通知。从下图就可以很明确的看出
接下来我们就开始分析EventBust的源码。
事件监听注册
我们比较熟悉eventbus的典型用法,其最具有代表性的入口就是:
EventBus.getDefault().register(this);
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<Method> subscriberMethods = findSubscriberMethods(subscriber.getClass(), methodName);
for (Method method : subscriberMethods) {
Class<?> eventType = method.getParameterTypes()[0];
subscribe(subscriber, method, eventType, threadMode);
}
}
从上面的代码可以看出register(Object subscriber)方法直接调用register(Object subscriber, String methodName, ThreadMode threadMode)方法,接下来我们说明一下这个方法的参数代表什么意思:
subscriber : 订阅者 在android中指的一般就是activity
methodName:传入的是defaultMethodName 这个是eventbus内部指定的默认的回调通知方法名,默认是 “onEvent”
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(subscriber.getClass(), methodName)方法我们移步进去看一下
//寻找订阅类中所有与指定方法名以及参数必须只有一个的方法并缓存起来避免多次查找
private List<Method> findSubscriberMethods(Class<?> subscriberClass, String methodName) {
String key = subscriberClass.getName() + '.' + methodName;
List<Method> subscriberMethods;
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = new ArrayList<Method>();
Class<?> clazz = subscriberClass;
HashSet<Class<?>> eventTypesFound = new HashSet<Class<?>>();
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;
}
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();
}
if (subscriberMethods.isEmpty()) {
throw new RuntimeException("Subscriber " + subscriberClass + " has no methods called " + methodName);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
这个方法比较长,但是整体逻辑还是比较简单的。这个方法主要是通过反射遍历订阅类以及其超类所有可以与指定方法名匹配并且参数只有一个的方法找到并添加到subscriberMethods集合返回,并把它缓存到methodCache map集合当中。这里被找到的方法是同名的,但是参数类型不同,这里的参数类型决定了将来要监听的事件类型。
好的,现在我们回到刚才调用findSubscriberMethods方法的地方继续往下走。接下来是遍历刚才findSubscriberMethods 返回的方法列表。这次遍历把Method的第一参数即事件类型当做参数传递给subscribe。
private void subscribe(Object subscriber, Method subscriberMethod, Class<?> eventType, ThreadMode threadMode) {
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
for (Subscription subscription : subscriptions) {
if (subscription.subscriber == subscriber) {
throw new RuntimeException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
}
subscriberMethod.setAccessible(true);
Subscription subscription = new Subscription(subscriber, subscriberMethod, threadMode);
subscriptions.add(subscription);
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
}
这个方法主要是通过eventType 从subscriptionsByEventType集合中取出对应的subscriptions,这里第一次subscriptions 肯定是null所以进行了实例化及初始化,如果注册过了再次注册的时候就会抛出异常,而subscriptions 中存放的是所有的观察者。,待会有事件发生时,根据事件的类型,从subscriptionsByEventType中取出所有的观察者subscriptions逐一通知。
subscriptions 初始化完成后再开始创建Subscription 了,Subscription 这个类是一个普通类里面就只有三个字段 subscriber(Activity),method(onEvent),ThreadMode(ThreadMode.MainThread);
到这里我们真个Eventbus注册的过程就算是分析完了
事件发送
接下来我们从事件发送的入口开始分析
public void post(Object event) {
List<Object> eventQueue = currentThreadEventQueue.get();
eventQueue.add(event);
BooleanWrapper isPosting = currentThreadIsPosting.get();
if (isPosting.value) {
return;
} else {
isPosting.value = true;
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0));
}
} finally {
isPosting.value = false;
}
}
}
这里有两个开始初始化的成员变量currentThreadIsPosting 和currentThreadEventQueue 这两个变量是关于线程的重要是使用了ThreadLocal的特性,如果大家对这一块不太了解下去可以查一查,我在这就不做过多的解释了。这里做了一些是否正在post和非空判断后最终调用了postSingleEvent()方法,接下来我们移步看一下这个真正的事件分发方法。
private void postSingleEvent(Object event) throws Error {
List<Class<?>> eventTypes = findEventTypes(event.getClass());
boolean subscriptionFound = false;
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
CopyOnWriteArrayList<Subscription> 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);
}
}
subscriptionFound = true;
}
}
if (!subscriptionFound) {
Log.d(TAG, "No subscripers registered for event " + event.getClass());
}
}
这个方法一进来就调用了findEventTypes方法我们先移步看一下findEventTypes这个方法具体做了什么操作。
private List<Class<?>> findEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<Class<?>>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
这个方法主要是根据传进来的eventClass检测我们定义的事件以及它的父类和实现的接口并把它添加到eventTypesCache这个集合中,并返回eventTypes。
让我们在回到postSingleEvent 方法中继续我们后续的分析:根据返回的事件列表遍历并从subscriptionsByEventType集合中找到注册过的说的观察着的集合subscriptions,然后在遍历进行事件的下发
事件的下发有两种模式:
- ThreadMode.PostThread
- ThreadMode.MainThread
我们前面提到过postThread模式下会通过反射直接将事件传递给观察者;mainThread模式的会先使用handler切换到主线程然后通过反射直接将事件传递给观察者。接下来我们就看一下是不是跟我猜想的是一样的。
...
mainThreadPoster = new PostViaHandler(Looper.getMainLooper());
...
static void postToSubscribtion(Subscription subscription, Object event) throws Error {
try {
subscription.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "
+ subscription.subscriber.getClass(), cause);
if (cause instanceof Error) {
throw (Error) cause;
}
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
final static class PostViaHandler extends Handler {
PostViaHandler(Looper looper) {
super(looper);
}
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");
}
}
@Override
public void handleMessage(Message msg) {
PendingPost pendingPost = (PendingPost) msg.obj;
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
postToSubscribtion(subscription, event);
}
}
果然我们可以看到它是用主线程的handler通过发送消息的方式把事件传递到主线程的。
Eventbus的反注册
接下来我们看一下EventBus的反注册方法,其实反注册就是把我们添加到集合缓存的那些对象逐级把它们给移除掉,要不然你的集合一直保存这个这些对象的引用导致这些对象无法回收导致内存泄漏问题。
public synchronized void unregister(Object subscriber, Class<?>... eventTypes) {
if (eventTypes.length == 0) {
throw new IllegalArgumentException("Provide at least one event class");
}
List<Class<?>> subscribedClasses = typesBySubscriber.get(subscriber);
if (subscribedClasses != null) {
for (Class<?> eventType : eventTypes) {
unubscribeByEventType(subscriber, eventType);
subscribedClasses.remove(eventType);
}
if (subscribedClasses.isEmpty()) {
typesBySubscriber.remove(subscriber);
}
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
以上就是我对EventBus1.0.1版本整个事件注册、发送以及反注册源码的分析,有什么不对的地方还请各位提出来我们一起探讨。