EventBus源码分析
来源:互联网 发布:手机照片整理软件 编辑:程序博客网 时间:2024/05/20 12:47
前言
这可能是东半球来的最晚的EventBus
分析文章了。
有多久没有写博客了?三个月?半年?想不清了,每天忙着做业务很少有自己的时间来总结和记录,感觉自己也变懒了,以前每天工作效率还是挺高的,现在已经不行了。
这两天空闲了些,但是学习不能停止呀,想想很多系统和三方库的源码都没有好好阅读和总结过,网上有很多分析的文章,但毕竟都别人写的。
纸上得来终觉浅,绝知此事要躬行。
看别人写的虽然也看得懂但是也很容易忘,自己看一遍然后写下来以后忘了再看一眼也很容易想起来吧。
使用
EventBus
的使用不多讲了,本篇文章基于EventBus2.4.0
分析。
注册事件
按照平时使用EventBus
的姿势,注册事件一般都是
EventBus.getDefault().register(this);
register
有很多重载方法,最后都会走到有3个参数的方法里:
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); } }
三个参数的说明如下:
Object subscriber
,要订阅广播的对象boolean sticky
,是否订阅粘性广播。所谓粘性广播是指广播在订阅之前已经发送,并保存在粘性Map中,当注册时如果选择sticky=true,则注册时立即从粘性Map中取出之前发送过的广播并分发到该对象。(具体可以参考EventBus粘性广播的介绍)int priority
,订阅优先级,数值越大优先级越高,越优先其他订阅者收到广播。
在上面的代码中:
第2行,先从订阅者中取出所有的订阅方法;
第3~5行,然后将这些方法信息保存到内存中。
我们来看findSubscriberMethods
方法:
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) { //省略此处代码:找出所有订阅方法并添加到subscriberMethods集合中 } 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; } }
第2行,取类名作为key。
第3~9行,从方法缓存methodCache
的map
中取该key对应的所有订阅方法,因为每次执行从类中找出所有的订阅方法后都会将所有找出来的方法存入methodCache
,下次要找的时候就不需要重新再找一遍了。
为什么一个类会被找两次呢?一个类被订阅两次难道不会抛出异常吗?事实上,我猜这个是针对遍历父类时来设计的,因为findSubscriberMethods
方法中不仅会找出该类中的所有订阅的方法,还会找出它所有父类的订阅方法。
假设有基类A,它有10个订阅方法,A有两个子类A1,A2,如果在A1,A2中都注册了订阅广播,那每次注册时都需要找出它们父类中的所有订阅方法,即基类A的10个方法要被找两次,如果有缓存的话,当A1注册时找出来基类A的10个方法后,A2再注册时就不需要重新找基类A的10个方法了,可以直接返回上一次的查找结果。
应该是这么个理吧。
第14~28行,是个大循环,精简一下如下:
//循环查找该类和其所有父类的所有onEvent方法 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) { //此处代码省略,主要是判断每个方法是否是onEvent方法并存入集合 } //查找父类 clazz = clazz.getSuperclass(); }
注释写的很清楚了,然后看一下for
循环里的东西:
// 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(); //如果方法名以onEvent开头 if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { int modifiers = method.getModifiers(); //如果该方法是public而且不是abstract或者static等类型 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //取出参数类型 Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { //截取方法名onEvent后面的部分 String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) {//如果方法名只是onEvent threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) {//onEventMainThread threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) {//onEventBackgroundThread threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) {//onEventAsync 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); } } }
找出方法后,将其封装成SubscriberMethod
对象,如下:
final class SubscriberMethod { final Method method; //具体方法 final ThreadMode threadMode; //订阅线程 final Class<?> eventType; //订阅的广播类型 /** Used for efficient comparison */ String methodString; //用于比较两个SubscriberMethod是否相等,源码有注释:Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6}
然后findSubscriberMethods
方法中后面的操作便是将查找出来的所有方法存入methodCache
作为缓存,并将所有的方法的集合作为返回值返回。
回到register
方法,
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); } }
找出订阅者中的所有onEvent
方法后,遍历所有SubscriberMethod
并在subscribe()
方法中对其进行分类。
subscribe
方法的作用是对SubscriberMethod
进行分类,并将相关信息整理保存到subscriptionsByEventType
和typesBySubscriber
这两个Map
中:
subscriptionsByEventType
,按广播类型进行分类,值是订阅该类型的所有订阅者。typesBySubscriber
,按订阅者分类,值是该类订阅的所有广播类型。
说起来有点绕,举个栗子:
//SimpleClass.javapublic class SimpleClass { public void register() { EventBus.getDefault().register(this); } public void unregister() { EventBus.getDefault().unregister(this); } public void onEvent(int params) { }}
//MainActivity.javapublic class MainActivity extends AppCompatActivity { private SimpleClass simpleClass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); simpleClass = new SimpleClass(); simpleClass.register(); EventBus.getDefault().register(this); } public void onEvent(String str) { } public void onEvent(int str) { } @Override protected void onDestroy() { super.onDestroy(); simpleClass.unregister(); EventBus.getDefault().unregister(this); }}
当执行完所有的subscribe
方法后,subscriptionsByEventType
和typesBySubscriber
中的数据如下:
subscriptionsByEventType
:key
为广播类型,value
为订阅了该广播类型的所有方法。
[ { "key":"java.lang.String", "value":[ { "active":true, "priority":0, "subscriber":"MainActivity@xxxx", "subscriberMethod":{ "eventType":"java.lang.String", "method":"public void com.xiaohongshu.eventbus.MainActivity.onEvent(java.lang.String)" } } ] }, { "key":"int", "value":[ { "active":true, "priority":0, "subscriber":"MainActivity@xxxx", "subscriberMethod":{ "eventType":"int", "method":"public void com.xiaohongshu.eventbus.MainActivity.onEvent(int)" } }, { "active":true, "priority":0, "subscriber":"SimpleClass@xxxx", "subscriberMethod":{ "eventType":"int", "method":"public void com.xiaohongshu.eventbus.MainActivity.onEvent(int)" } } ] }]
typesBySubscriber
:key
为所有注册过的订阅者,value
为该订阅者的所有订阅方法。
[ { "key":"MainActivity", "value":[ "int", "java.lang.String" ] }, { "key":"SimpleClass", "value":[ "int" ] }]
明白这两个map
的作用,接下来就好分析啦。看代码:
// Must be called in synchronized block private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class<?> eventType = subscriberMethod.eventType; //先从subscriptionsByEventType中取出该广播类型对应的所有订阅者 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) { 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()); } } }
注释写的很清楚了,不解释。
分发事件
按照平时使用EventBus
的姿势,发送事件一般都是
EventBus.getDefault().post(this);
post
方法也有很多个,先看其中一个:
/** Posts the given event to the event bus. */ 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 { //标志正在发送状态为false postingState.isPosting = false; //标志不是主线程 postingState.isMainThread = false; } } }
第3行,取出该线程的状态。这里用到了ThreadLocal
,熟悉Handler
机制的人一定不会觉得陌生,ThreadLocal
的作用是保证每份线程都能维持不同的实例对象,因为线程状态是跟线程相关的,所以这里用ThreadLocal
再合适不过了。
而PostingThreadState
中的内容如下:
/** For ThreadLocal, much faster to set (and get multiple values). */ final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<Object>();//事件队列 boolean isPosting;//是否正在发送事件 boolean isMainThread;//是否是主线程 Subscription subscription;//当前订阅者 Object event;//当前分发的事件 boolean canceled;//该线程是否已经取消订阅 }
回到post
方法中,真正发送事件的是postSingleEvent
方法。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //是否查找继承关系,EventBusBuilder中eventInheritance默认为true 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)); } } }
在postSingleEvent
中又进行了很多判断,最后由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; }
可以看到,又进行了很多判断,然后在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); } }
然后又判断了很多线程,根据订阅者期望的线程分发给对应的分发线程,最后在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); } }
通过反射调用,很明朗。
注意在postToSubscription
方法中分发事件时:
- 如果订阅者期望是主线程而当前如果不是主线程,则是将事件插入到
mainThreadPoster
中; - 如果订阅者期望是后台线程,则将事件插入后台事件队列
BackgroundThread
; - 如果订阅者期望是异步线程,则将事件插入异步事件队列
asyncPoster
; - 其他情况则直接分发。
另外,补充一下几个线程大致的实现方式:
private final HandlerPoster mainThreadPoster; private final BackgroundPoster backgroundPoster; private final AsyncPoster asyncPoster; private final ExecutorService executorService;
mainThreadPoster
,Handler
的子类对象,还是基于Handler
的那一套机制,只是Looper
默认是Looper.getMainLooper()
backgroundPoster
和asyncPoster
都是实现的Runnable
接口,然后作为线程池executorService
的参数来运行executorService
默认是EventBusBuilder
中提供实现,默认是一个缓存线程池:
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
至此,事件发送的也介绍完了。
取消注册
按照平时使用EventBus
的姿势,取消注册一般都是
EventBus.getDefault().unregister(this);
之前介绍过两个map
:subscriptionsByEventType
和typesBySubscriber
。
subscriptionsByEventType
,按广播类型进行分类,值是订阅该类型的所有订阅者。typesBySubscriber
,按订阅者分类,值是该类订阅的所有广播类型。
发送广播时,可以根据subscriptionsByEventType
找出所有的订阅者,然后将该事件依次发送给它们。
那typesBySubscriber
的作用是什么呢?现在想取消注册的话就可以看到它的功效啦,如果想取消订阅该订阅者订阅的所有广播,则只需要根据typesBySubscriber
找出该订阅者订阅过的所有广播,然后将其从subscriptionsByEventType
中移除就行啦。
看unregister
代码,
/** Unregisters the given subscriber from all event classes. */ public synchronized void unregister(Object subscriber) { //找出该订阅者订阅的所有广播 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class<?> eventType : subscribedTypes) { //依次取消订阅该广播 unubscribeByEventType(subscriber, eventType); } //将其从已订阅者集合中移除 typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
unregister
中用到了unubscribeByEventType
,代码如下:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ private void unubscribeByEventType(Object subscriber, Class<?> eventType) { //取出所有订阅了该广播的订阅者 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); for (int i = 0; i < size; i++) { Subscription subscription = subscriptions.get(i); //如果当前订阅者等于待取消注册的订阅者,则取消订阅 if (subscription.subscriber == subscriber) { subscription.active = false; subscriptions.remove(i); i--; size--; } } } }
至此,取消订阅的操作也完成啦。
- EventBus 2.4 源码分析
- EventBus源码注释分析
- EventBus框架源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus 3 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 3.0 源码分析
- EventBus 3.0 源码分析
- EventBus源码分析
- React Native 初学-新建HelloWorldApp工程
- 企业级云管理平台的架构实现与落地实践、趋势分析
- NGUI根据点击按钮名字获取想要的值
- 如何进行CRC校验
- 使用Vue.js完成一个todo-list
- EventBus源码分析
- Linux-安装vsftp
- golang 令牌桶限速器实现(ratelimit)
- MySql 查询实例
- 蓝桥杯 带分数
- Eclipse快捷键
- 性能测试方案设计的方法和思路
- Dubbo消费者无法连接到生产者提供的服务
- beecloud轻松实现支付