源码解析EventBus
2015-11-27 20:35
453 查看
EventBus通过订阅者/发布者机制使得整个系统的耦合度更加的松散,在不使用Handler和AsyncTask的情况下也能实现对界面的更新,在这篇文章中主要对EventBus源码进行一个简要分析,如果还没有接触过这个开源库可以参考一下官方给出的实例代码~
还是从我们平时使用的地方开始说起。在使用时我们的入口都是EventBus类的静态方法,那么最常接触的就是getDefault方法了,来看一下:
构造方法采用了单例模式的双重检查来保证单例的唯一性,再看一下defaultInstance:
将实例声明为volatile来保证了及时可见性。
在这里调用了EventBus的构造方法,我们就不再跟进构造方法中了,由于其构造涉及到的参数数量较多,所以在这里采取了构造者模式,有兴趣可以去看一下实现(链式)。
看过了getDefault方法,如果看过例子都应该了解,我们还
4000
需要在初始化时注册当前的类,并且在销毁时反注册掉当前类,涉及到注册的方法有如下几个:
这里的subscriber显然就是订阅者类啦,priority是订阅者是优先级,至于Sticky我们暂时先不去考虑它。这四个方法都调用了同一个方法:
在这里调用了subscriberMethodFinder对象的findSubscriberMethod方法,传入的是当前的订阅者Class对象。可以看到subscriberMethodFinder是在构造方法中创建出来的,我们直接来看它的findSubscriberMethod方法:
方法很长但是并不复杂,一点一点来看一下:
首先得到了订阅者类的全类名,而后以这个全类名为key查询methodCach,下面是这个对象的定义:
很显然第一次在缓存中是一定找不到的,我们继续向下看。
创建一个ArrayList,从对象名字可以看出这是一个订阅者方法的集合,而后进入while循环,判断一下当前的订阅者类是不是一个系统中的提供类,若是的话直接就终止掉循环。
然后获取到当前类的所有方法,包括私有方法,但是在这里作者注释声明了在这个版本中只允许订阅者onEvent开头的方法为public,这点在后文中将会有所体现。
得到方法的参数,当方法的参数不是一个的时候抛出异常。
截取该类中以onEvent开头的方法名,并且使用ThreadMode来储存方法的名字。
后面进行了一个判定的操作然后将方法method,保存信息的threadMode和方法参数Class对象封装成一个SubscriberMethod类的对象并且存放在subscriberMethods集合中。
而后将clazz对象置为父类重新进行循环。
最后可以看到以当前订阅者类全名为key存入到了缓存中,方便下次使用,而后返回subscriberMethods集合也就是订阅者方法集合。
到这里findSubscriberMethods就结束了,我们回到主线上继续看:
循环出了subscriberMethods集合中的每一个方法然后调用subscribe方法,我们进入看一下:
首先得到该方法参数的Class对象,然后以这个对象作为key从subscriptionsByEventType集合中来找出value,还是看一下定义:
先简单的从名字理解一下,以onEvent开头的方法参数类型为key的map,保存了所有包含这种参数类型的Subscription集合(Subscription是由当前订阅者,订阅者的单独一个方法和订阅者的优先级共同确定的)。
在第一次取出来的时候也是一定会取出一个null,这时候将创建这个集合并且将其放入map。
这时候将之前创建出的Subscription类的对象添加到集合当中。在这里作者将Subscription类对象中封装的方法设置权限的代码注释掉了,也是应了刚才说的,只允许onEvent开头的方法是public的。
而后又出现了一个typesBySubscriber,看一下它的定义:
从名字也可以很简单的看出来,它保存了当前订阅者对象所有可能接受的订阅事件。
以当前订阅者对象为key来从这个map中找出集合,如果不存在就创建一个,然后把eventType订阅事件放到里面。剩下的一些代码也暂时不去考虑,先只看一下主线。
到这里注册的逻辑就已经完成了,看起来还是挺简单的,只是将订阅者啊,Subscription向集合里加一加。看完了注册我们再来看一下发布事件的post方法:
首先在currentPostingThreadState调用get方法,看一下这个对象的定义:
是一个ThreadLocal,那么很显然每个线程都将维护它自己的postingThreadState对象。
从这个对象中得到当前的事件队列然后将发布的事件放到队列中。
看一下当前如果没有处于正在发布的状态,下行。
保存一下当前是否是在主线程中进行的事件发布,并且将状态置为正在发布。
而后不断的从队列中取出事件调用postSingleEvent方法,进入方法看一下:
在这里我们只看主线的状态,当最简单的情况下应该走else中的方法,也就是postSingleEventForEventType,再进入看一下:
以当前发布的事件为key,从subscriptionsByEventType中找出Subscription的集合,如果把subscriptionsByEventType忘记了可以回到前面再看一下。
然后循环出这个集合中的每个Subscription类的对象调用postToSubscription方法,我们来看一下:
判断一下Subscription类对象中封装的方法中ThreadMode,如果是PostThread那么直接调用invokeSubscriber,看一下:
简单的通过反射来调用了onEventPostThread方法,并且将event事件传入进去。值得注意的是这里的postToSubscription方法是在事件发布的线程中调用的。
如果switch选择出来的是MainThread,那么判断当前发布事件的线程是否是主线程,如果是直接执行,不是将subscription和event事件入队mainThreadPoster。
同理BackgroundThread判断当前是否是主线程,是的话入队backgroundPoster,不是的话直接调用。
如果是Async则直接入队asyncPoster。
如果有兴趣可以查看一下mainThreadPoster、backgroundPoster和asyncPoster的内容,里面的实现也比较简单,这里就不再赘述。
还是从我们平时使用的地方开始说起。在使用时我们的入口都是EventBus类的静态方法,那么最常接触的就是getDefault方法了,来看一下:
public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
构造方法采用了单例模式的双重检查来保证单例的唯一性,再看一下defaultInstance:
static volatile EventBus defaultInstance;
将实例声明为volatile来保证了及时可见性。
在这里调用了EventBus的构造方法,我们就不再跟进构造方法中了,由于其构造涉及到的参数数量较多,所以在这里采取了构造者模式,有兴趣可以去看一下实现(链式)。
看过了getDefault方法,如果看过例子都应该了解,我们还
4000
需要在初始化时注册当前的类,并且在销毁时反注册掉当前类,涉及到注册的方法有如下几个:
public void register(Object subscriber) { register(subscriber, false, 0); } public void register(Object subscriber, int priority) { register(subscriber, false, priority); } public void registerSticky(Object subscriber) { register(subscriber, true, 0); } public void registerSticky(Object subscriber, int priority) { register(subscriber, true, priority); }
这里的subscriber显然就是订阅者类啦,priority是订阅者是优先级,至于Sticky我们暂时先不去考虑它。这四个方法都调用了同一个方法:
private synchronized void register(Object subscriber, boolean sticky, int priority) { List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); } }
在这里调用了subscriberMethodFinder对象的findSubscriberMethod方法,传入的是当前的订阅者Class对象。可以看到subscriberMethodFinder是在构造方法中创建出来的,我们直接来看它的findSubscriberMethod方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { String key = subscriberClass.getName(); 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.getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { 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(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(); } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + ON_EVENT_METHOD_NAME); } else { synchronized (methodCache) { methodCache.put(key, subscriberMethods); } return subscriberMethods; } }
方法很长但是并不复杂,一点一点来看一下:
首先得到了订阅者类的全类名,而后以这个全类名为key查询methodCach,下面是这个对象的定义:
private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();
很显然第一次在缓存中是一定找不到的,我们继续向下看。
创建一个ArrayList,从对象名字可以看出这是一个订阅者方法的集合,而后进入while循环,判断一下当前的订阅者类是不是一个系统中的提供类,若是的话直接就终止掉循环。
然后获取到当前类的所有方法,包括私有方法,但是在这里作者注释声明了在这个版本中只允许订阅者onEvent开头的方法为public,这点在后文中将会有所体现。
得到方法的参数,当方法的参数不是一个的时候抛出异常。
截取该类中以onEvent开头的方法名,并且使用ThreadMode来储存方法的名字。
后面进行了一个判定的操作然后将方法method,保存信息的threadMode和方法参数Class对象封装成一个SubscriberMethod类的对象并且存放在subscriberMethods集合中。
而后将clazz对象置为父类重新进行循环。
最后可以看到以当前订阅者类全名为key存入到了缓存中,方便下次使用,而后返回subscriberMethods集合也就是订阅者方法集合。
到这里findSubscriberMethods就结束了,我们回到主线上继续看:
循环出了subscriberMethods集合中的每一个方法然后调用subscribe方法,我们进入看一下:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { 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); 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) { if (eventInheritance) { // Existing sticky events of all subclasses of eventType have to be considered. // Note: Iterating over all events may be inefficient with lots of sticky events, // thus data structure should be changed to allow a more efficient lookup // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
首先得到该方法参数的Class对象,然后以这个对象作为key从subscriptionsByEventType集合中来找出value,还是看一下定义:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
先简单的从名字理解一下,以onEvent开头的方法参数类型为key的map,保存了所有包含这种参数类型的Subscription集合(Subscription是由当前订阅者,订阅者的单独一个方法和订阅者的优先级共同确定的)。
在第一次取出来的时候也是一定会取出一个null,这时候将创建这个集合并且将其放入map。
这时候将之前创建出的Subscription类的对象添加到集合当中。在这里作者将Subscription类对象中封装的方法设置权限的代码注释掉了,也是应了刚才说的,只允许onEvent开头的方法是public的。
而后又出现了一个typesBySubscriber,看一下它的定义:
private final Map<Object, List<Class<?>>> typesBySubscriber;
从名字也可以很简单的看出来,它保存了当前订阅者对象所有可能接受的订阅事件。
以当前订阅者对象为key来从这个map中找出集合,如果不存在就创建一个,然后把eventType订阅事件放到里面。剩下的一些代码也暂时不去考虑,先只看一下主线。
到这里注册的逻辑就已经完成了,看起来还是挺简单的,只是将订阅者啊,Subscription向集合里加一加。看完了注册我们再来看一下发布事件的post方法:
public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
首先在currentPostingThreadState调用get方法,看一下这个对象的定义:
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };
是一个ThreadLocal,那么很显然每个线程都将维护它自己的postingThreadState对象。
从这个对象中得到当前的事件队列然后将发布的事件放到队列中。
看一下当前如果没有处于正在发布的状态,下行。
保存一下当前是否是在主线程中进行的事件发布,并且将状态置为正在发布。
而后不断的从队列中取出事件调用postSingleEvent方法,进入方法看一下:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); bd7c boolean subscriptionFound = false; if (eventInheritance) { 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) { Log.d(TAG, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
在这里我们只看主线的状态,当最简单的情况下应该走else中的方法,也就是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 = false; try { postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; } finally { postingState.event = null; postingState.subscription = null; postingState.canceled = false; } if (aborted) { break; } } return true; } return false; }
以当前发布的事件为key,从subscriptionsByEventType中找出Subscription的集合,如果把subscriptionsByEventType忘记了可以回到前面再看一下。
然后循环出这个集合中的每个Subscription类的对象调用postToSubscription方法,我们来看一下:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case PostThread: invokeSubscriber(subscription, event); break; case MainThread: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BackgroundThread: 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); } }
判断一下Subscription类对象中封装的方法中ThreadMode,如果是PostThread那么直接调用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); } }
简单的通过反射来调用了onEventPostThread方法,并且将event事件传入进去。值得注意的是这里的postToSubscription方法是在事件发布的线程中调用的。
如果switch选择出来的是MainThread,那么判断当前发布事件的线程是否是主线程,如果是直接执行,不是将subscription和event事件入队mainThreadPoster。
同理BackgroundThread判断当前是否是主线程,是的话入队backgroundPoster,不是的话直接调用。
如果是Async则直接入队asyncPoster。
如果有兴趣可以查看一下mainThreadPoster、backgroundPoster和asyncPoster的内容,里面的实现也比较简单,这里就不再赘述。
相关文章推荐
- Android 中 EventBus 的使用之多线程事件处理
- EventBus
- EventBus 学习
- EventBus 学习进阶
- eventbus简单使用
- EventBus的使用及 解决异常Subscriber ****has no public methods called **
- EventBus使用详解(一)——初步使用EventBus
- EventBus使用详解(二)——EventBus使用进阶
- Event Bus在安卓开发中的使用场景和技巧
- EventBus学习笔记
- eventbus使用
- EventBus实例
- 开源框架EventBus的使用
- Android 消息处理源码分析(2)
- 从源码角度解析Android事件分发机制
- 使用Event Bus模式解耦Android App组件间通信
- guava_事件总线
- Android开发中无处不在的设计模式——观察者模式
- 教你自己实现一个事件总线EventBus
- EventBus框架原理解析(结合源码)(上)