EventBus源码分析(一):入口函数提纲挈领(2.4版本)
2016-07-01 15:58
513 查看
EventBus源码分析(一):入口函数提纲挈领(2.4版本)
EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
EventBus源码分析(四):线程模型分析(2.4版本)
EventBus源码解读详细注释(1)register的幕后黑手
EventBus源码解读详细注释(2)MainThread线程模型分析
EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别
EventBus源码解读详细注释(4)register时刷新的两个map
EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义
EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!
本文是EventBus源码分析的第一篇文章,不拘泥于具体的实现细节,从宏观上把握EventBus的设计思路,而EventBus总体的设计思路为:
EventBus实例保存了事件到订阅者列表的map,发布事件的时候,从该map中取出该事件的所有订阅者,在规定的线程中反射调用所有订阅者的事件处理方法。
静态易变的defaultInstance属性
静态的getDefault方法
但是EventBus并非严格的DCL单例模式,因为构造器并不是私有的,因此并不能保证EventBus实例的唯一性。不同实例之间是总线隔离的,即EventBus实例A发布的事件event并不能被EventBus实例B订阅接受。
因此register主要分为两步:
找到订阅者订阅的所有事件
对每个订阅者订阅的所有事件,逐一进行订阅
EventBus针对每一个事件,维护了一个订阅了这个事件的订阅者的列表,并且用HashMap来保存,HashMap的键为事件的Class,值为该事件的订阅者列表。
该HashMap有两点需要注意:
该HashMap是对象属性不是类属性,因此不同的EventBus实例的该属性是不同的,这也是不同EventBus实例之间总线隔离的原因
订阅者列表并没有通过ArrayList实现,为了达到线程并发安全,采用CopyOnWriteArrayList
post方法首先获取了事件发布者的线程状态,从该线程状态中获取事件队列,然后不停得处理队列中的事件,直到队列为空
postSingleEvent方法里边进一步调用了postSingleEventForEventType方法,找到该事件的订阅者列表
首先在同步代码块中获取该事件的订阅者列表
然后遍历订阅者列表,对每一个订阅者进行处理
而postToSubscription方法就是根据不同的线程模式,在不同的线程中反射调用订阅者的事件处理方法。
在register注册的时候,保存了两个重要的HashMap,其中一个就是上文提到的事件到订阅者列表的map,该map用于发布事件的时候寻找该事件的订阅者列表,然后通过反射调用订阅者的事件处理方法。第二个就是订阅者到事件列表的map,表明了该订阅者订阅了哪些事件,主要用于解注册。
unregister方法先从此map中找到该订阅者订阅的所有事件
然后对每一个事件进行解注册
最后从此map中删除该订阅者
本文结束,接下来会深入源码,从细节上探索EventBus的设计思路。
EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
EventBus源码分析(四):线程模型分析(2.4版本)
EventBus源码解读详细注释(1)register的幕后黑手
EventBus源码解读详细注释(2)MainThread线程模型分析
EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别
EventBus源码解读详细注释(4)register时刷新的两个map
EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义
EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!
本文是EventBus源码分析的第一篇文章,不拘泥于具体的实现细节,从宏观上把握EventBus的设计思路,而EventBus总体的设计思路为:
EventBus实例保存了事件到订阅者列表的map,发布事件的时候,从该map中取出该事件的所有订阅者,在规定的线程中反射调用所有订阅者的事件处理方法。
DCL单例创建EventBus对象
EventBus提供了静态方法创建EventBus对象,实现方式为标准的DCL双检锁的单例模式。DCL双检锁分为两部分:静态易变的defaultInstance属性
静态的getDefault方法
... static volatile EventBus defaultInstance; ... /** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
但是EventBus并非严格的DCL单例模式,因为构造器并不是私有的,因此并不能保证EventBus实例的唯一性。不同实例之间是总线隔离的,即EventBus实例A发布的事件event并不能被EventBus实例B订阅接受。
register方法向EventBus注册订阅者
register(this)就是在当前类(Activity等)遍历所有方法,按照EventBus事件处理方法的命名规则约束(onEvent开头,只有一个参数,非static,非abstract的public方法)过滤出事件处理方法然后进行存储。因此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); } ` }
EventBus针对每一个事件,维护了一个订阅了这个事件的订阅者的列表,并且用HashMap来保存,HashMap的键为事件的Class,值为该事件的订阅者列表。
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
该HashMap有两点需要注意:
该HashMap是对象属性不是类属性,因此不同的EventBus实例的该属性是不同的,这也是不同EventBus实例之间总线隔离的原因
订阅者列表并没有通过ArrayList实现,为了达到线程并发安全,采用CopyOnWriteArrayList
post方法向EventBus发布事件
EventBus存储了事件到及该事件的订阅者列表,因此发布事件就应该是找到该事件的订阅者列表,对每一个订阅者通过反射调用事件处理方法。/** 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 { postingState.isPosting = false; postingState.isMainThread = false; } } }
post方法首先获取了事件发布者的线程状态,从该线程状态中获取事件队列,然后不停得处理队列中的事件,直到队列为空
while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0), postingState); }
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; }
首先在同步代码块中获取该事件的订阅者列表
synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); }
然后遍历订阅者列表,对每一个订阅者进行处理
for (Subscription subscription : subscriptions) { ... postToSubscription(subscription, event, postingState.isMainThread); ... }
而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); } }
unregister方法-订阅者向EventBus解注册
为了防止内存泄露,像Android中的广播或者其他的订阅者模式,有注册就有解注册,EventBus也不例外。unregister方法一般在与调用register方法对称的生存周期回调中调用。/** 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()); } }
在register注册的时候,保存了两个重要的HashMap,其中一个就是上文提到的事件到订阅者列表的map,该map用于发布事件的时候寻找该事件的订阅者列表,然后通过反射调用订阅者的事件处理方法。第二个就是订阅者到事件列表的map,表明了该订阅者订阅了哪些事件,主要用于解注册。
private final Map<Object, List<Class<?>>> typesBySubscriber;
unregister方法先从此map中找到该订阅者订阅的所有事件
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
然后对每一个事件进行解注册
for (Class<?> eventType : subscribedTypes) { unubscribeByEventType(subscriber, eventType); }
最后从此map中删除该订阅者
typesBySubscriber.remove(subscriber);
本文结束,接下来会深入源码,从细节上探索EventBus的设计思路。
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- 浅析Ruby的源代码布局及其编程风格
- asp.net 抓取网页源码三种实现方法
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- ASP.NET使用HttpWebRequest读取远程网页源代码
- PHP网页游戏学习之Xnova(ogame)源码解读(六)
- C#获取网页HTML源码实例
- PHP网页游戏学习之Xnova(ogame)源码解读(八)
- PHP网页游戏学习之Xnova(ogame)源码解读(四)
- 深入理解PHP之源码目录结构与功能说明
- JS小游戏之极速快跑源码详解
- JS小游戏之象棋暗棋源码详解
- android源码探索之定制android关机界面的方法
- 基于Android设计模式之--SDK源码之策略模式的详解
- Android游戏源码分享之2048
- C语言借助EasyX实现的生命游戏源码