EventBus用法及源码解析php
目录介绍html
1.EventBus简介git
1.1 EventBus的三要素github
1.2 EventBus的四种ThreadMode(线程模型)缓存
1.3 EventBus怎么调用并发
2.EventBus使用app
2.1 最简单的使用async
3.EventBus注册源码解析ide
3.1 EventBus.getDefault()获取对象函数
3.2 register(this)注册源码解析
3.2.1 首先看register(this)源码
3.2.2 接下来看findSubscriberMethods(subscriberClass)里面的源码
3.2.3 接下来看findUsingInfo(subscriberClass)源码
3.3 查找完全部的订阅方法后便开始对全部的订阅方法进行注册
3.3.1 subscribe(subscriber, subscriberMethod);
3.3.2 订阅者的注册过程
3.3.3 流程图
4.EventBus事件分发解析
4.1 从post方法入手
4.2 什么是PostingThreadState?
4.3 PostingThreadState怎么得到?
4.4 来看看postSingleEvent方法里作了什么
4.5 接下来看看postSingleEventForEventType方法
4.6 接下来看看postToSubscription方法
4.7 整个流程图
4.8 总结一下整个事件分发的过程
5.EventBus取消注册解析
5.1 unregister(this)方法入手
5.2 再来看看unsubscribeByEventType(subscriber, eventType)
5.3 取消注册流程图
5.4 总结一下取消注册的过程
6.总结一下EventBus的工做原理
6.1 订阅逻辑
6.2 事件发送逻辑
6.3 取消逻辑
6.4 利与弊
7.其余介绍
7.1 参考文档
7.2 其余
0.本人写的综合案例
模块:新闻,音乐,视频,图片,唐诗宋词,快递,天气,记事本,阅读器等等
接口:七牛,阿里云,天行,干货集中营,极速数据,追书神器等等
持续更新目录说明:http://www.jianshu.com/p/5301...
1.EventBus简介
1.1 EventBus的三要素
Event:事件
能够是任意类型的对象。
Subscriber:事件订阅者
在EventBus3.0以前,消息处理的方法只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,他们分别表明四种线程模型。
在EventBus3.0以后,事件处理的方法能够随便取名,可是须要添加一个注解@Subscribe,而且要指定线程模型(默认为POSTING),四种线程模型下面会讲到。
Publisher:事件发布者
能够在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法。能够本身实例化EventBus对象,但通常使用EventBus.getDefault()就行了,根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
1.2 EventBus的四种ThreadMode(线程模型)
POSTING(默认):
若是使用事件处理函数指定了线程模型为POSTING,那么该事件在哪一个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为POSTING的事件处理函数中尽可能避免执行耗时操做,由于它会阻塞事件的传递,甚至有可能会引发ANR。
MAIN:
事件的处理会在UI线程中执行。事件处理时间不能太长,长了会ANR的。
BACKGROUND:
若是事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,若是事件原本就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操做。
ASYNC:
不管事件在哪一个线程发布,该事件处理函数都会在新建的子线程中执行,一样,此事件处理函数中禁止进行UI更新操做。
1.3 EventBus怎么调用**
代码以下: EventBus.getDefault().post(param);
调用原理简单理解为:
一句话,你也能够叫发布,只要把这个param发布出去,EventBus会在它内部存储的方法中,进行扫描,找到参数匹配的,就使用反射进行调用。
撇开专业术语:其实EventBus就是在内部存储了一堆onEvent开头的方法,而后post的时候,根据post传入的参数,去找到匹配的方法,反射调用之。
它内部使用了Map进行存储,键就是参数的Class类型。知道是这个类型,那么你以为根据post传入的参数进行查找仍是个事么?
2.EventBus使用
2.1 最简单的使用
2.1.1 自定义一个事件类
public class MessageEvent { }
2.1.2 在须要订阅事件的地方注册事件
EventBus.getDefault().register(this);
2.1.3 发送事件
EventBus.getDefault().post(messageEvent);
2.1.4 处理事件
3.0以后, 消息处理的方法能够随便取名
问题: (threadMode = ThreadMode.MAIN)是作什么用的??
须要添加一个注解@Subscribe,而且要指定线程模型
若是没有添加,那就是默认为POSTING
@Subscribe(threadMode = ThreadMode.MAIN) public void Hhhh(MessageEvent messageEvent) { ... }
2.1.5 取消事件订阅
EventBus.getDefault().unregister(this);
3.EventBus注册源码解析
3.1 EventBus.getDefault()获取对象
a.先看源码:
/** 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; }
b.分析
单例模式, 使用了双重判断的方式,防止并发的问题,还能极大的提升效率
3.2 register(this)注册源码解析
3.2.1.首先看register(this)源码
public void register(Object subscriber) { //首先获取订阅者的类对象 Class<?> subscriberClass = subscriber.getClass(); //用 subscriberMethodFinder 提供的方法,找到在 subscriber 这个类里面订阅的内容。 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { // for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
3.2.2.接下来看findSubscriberMethods(subscriberClass)里面的源码
该方法的做用其实就是从订阅类中获取全部的订阅方法信息
findSubscriberMethods找出一个SubscriberMethod的集合,也就是传进来的订阅者全部的订阅的方法,接下来遍历订阅者的订阅方法来完成订阅者的订阅操做。对于SubscriberMethod(订阅方法)类中,主要就是用保存订阅方法的Method对象、线程模式、事件类型、优先级、是不是粘性事件等属性。
源码分析:首先从缓存中查找,若是找到了就立马返回。若是缓存中没有的话,则根据 ignoreGeneratedIndex 选择如何查找订阅方法,ignoreGeneratedIndex属性表示是否忽略注解器生成的MyEventBusIndex。最后,找到订阅方法后,放入缓存,以避免下次继续查找。ignoreGeneratedIndex 默认就是false,能够经过EventBusBuilder来设置它的值。咱们在项目中常常经过EventBus单例模式来获取默认的EventBus对象,也就是ignoreGeneratedIndex为false的状况,这种状况调用了findUsingInfo方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //首先从缓存中读取 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } //是否忽略注解器生成的MyEventBusIndex类 if (ignoreGeneratedIndex) { //利用反射来获取订阅类中的订阅方法信息 subscriberMethods = findUsingReflection(subscriberClass); } else { //从注解器生成的MyEventBusIndex类中得到订阅类的订阅方法信息 subscriberMethods = findUsingInfo(subscriberClass); } //在得到subscriberMethods之后,若是订阅者中不存在@Subscribe注解而且为public的订阅方法,则会抛出异常。 if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { //保存进缓存 METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } } //METHOD_CACHE,是一个map集合,键是class类型 Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
3.2.3 接下来看findUsingInfo(subscriberClass)源码
经过getSubscriberInfo方法来获取订阅者信息。在咱们开始查找订阅方法的时候并无忽略注解器为咱们生成的索引MyEventBusIndex,若是咱们经过EventBusBuilder配置了MyEventBusIndex,便会获取到subscriberInfo,调用subscriberInfo的getSubscriberMethods方法即可以获得订阅方法相关的信息,这个时候就不在须要经过注解进行获取订阅方法。若是没有配置MyEventBusIndex,便会执行findUsingReflectionInSingleClass方法,将订阅方法保存到findState中。最后再经过getMethodsAndRelease方法对findState作回收处理并反回订阅方法的List集合。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { //获取订阅者信息,没有配置MyEventBusIndex返回null findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { //经过反射来查找订阅方法 findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
3.3 查找完全部的订阅方法后便开始对全部的订阅方法进行注册
3.3.1 回到
register(this)这个方法
3.3.2 订阅者的注册过程
订阅的代码主要就作了两件事,第一件事是将咱们的订阅方法和订阅者封装到subscriptionsByEventType和typesBySubscriber中,subscriptionsByEventType是咱们投递订阅事件的时候,就是根据咱们的EventType找到咱们的订阅事件,从而去分发事件,处理事件的;typesBySubscriber在调用unregister(this)的时候,根据订阅者找到EventType,又根据EventType找到订阅事件,从而对订阅者进行解绑。第二件事,若是是粘性事件的话,就立马投递、执行。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //获取订阅方法的参数类型 Class<?> eventType = subscriberMethod.eventType; //根据订阅者和订阅方法构造一个订阅事件 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //获取当前订阅事件中Subscription的List集合 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //该事件对应的Subscription的List集合不存在,则从新建立并保存在subscriptionsByEventType中 if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { //订阅者已经注册则抛出EventBusException if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //遍历订阅事件,找到比subscriptions中订阅事件小的位置,而后插进去 int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } //经过订阅者获取该订阅者所订阅事件的集合 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } //将当前的订阅事件添加到subscribedEvents中 subscribedEvents.add(eventType); if (subscriberMethod.sticky) { if (eventInheritance) { //粘性事件的处理 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); } } }
3.3.3 流程图
4.EventBus事件分发解析
4.1 从post方法入手
首先从PostingThreadState对象中取出事件队列,而后再将当前的事件插入到事件队列当中。最后将队列中的事件依次交由postSingleEvent方法进行处理,并移除该事件。
/** Posts the given event to the event bus. */ public void post(Object event) { //获取当前线程的postingState PostingThreadState postingState = currentPostingThreadState.get(); //取得当前线程的事件队列 List<Object> eventQueue = postingState.eventQueue; //将该事件添加到当前的事件队列中等待分发 eventQueue.add(event); if (!postingState.isPosting) { //判断是不是在主线程post 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; } } }
4.2 什么是PostingThreadState?
PostingThreadState中包含了当前线程的事件队列,就是当前线程全部分发的事件都保存在eventQueue事件队列中以及订阅者订阅事件等信息,有了这些信息咱们就能够从事件队列中取出事件分发给对应的订阅者
final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<Object>();//当前线程的事件队列 boolean isPosting;//是否有事件正在分发 boolean isMainThread;//post的线程是不是主线程 Subscription subscription;//订阅者 Object event;//订阅事件 boolean canceled;//是否取消 }
4.3 PostingThreadState怎么得到?
ThreadLocal 是一个线程内部的数据存储类,经过它能够在指定的线程中存储数据,而这段数据是不会与其余线程共享的。
能够看出currentPostingThreadState的实现是一个包含了PostingThreadState的ThreadLocal对象,这样能够保证取到的都是本身线程对应的数据。
咱们有了PostingThreadState获取到了当前线程的事件队列,接下来就是事件分发,咱们来看postSingleEvent(eventQueue.remove(0), postingState);
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };
4.4 来看看postSingleEvent方法里作了什么
eventInheritance表示是否向上查找事件的父类,它的默认值为true,能够经过在EventBusBuilder中来进行配置。当eventInheritance为true时,则经过lookupAllEventTypes找到全部的父类事件并存在List中,而后经过postSingleEventForEventType方法对事件逐一处理,接下来看看postSingleEventForEventType方法
事件分发 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { //获得事件类型 Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //是否触发订阅了该事件(eventClass)的父类,以及接口的类的响应方法. 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)); } } }
4.5 接下来看看postSingleEventForEventType方法
同步取出该事件对应的Subscription集合并遍历该集合将事件event和对应Subscription传递给postingState并调用postToSubscription方法对事件进行处理,接下来看看postToSubscription方法:
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; }
4.6 接下来看看postToSubscription方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case POSTING://默认的 ThreadMode,表示在执行 Post 操做的线程直接调用订阅者的事件响应方法, //不论该线程是否为主线程(UI 线程)。 invokeSubscriber(subscription, event); break; case MAIN://在主线程中执行响应方法。 if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BACKGROUND://在后台线程中执行响应方法。 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); } }
4.7 整个流程图
4.8 总结一下整个事件分发的过程
首先获取当前线程的PostingThreadState对象从而获取到当前线程的事件队列
经过事件类型获取到全部订阅者集合
经过反射执行订阅者中的订阅方法
5.EventBus取消注册解析
5.1 unregister(this)方法入手
/** 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) { //从事件类型的订阅者集合中移除订阅者 unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
5.2 再来看看unsubscribeByEventType(subscriber, eventType)
private void unsubscribeByEventType(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--; } } } }
5.3 取消注册流程图
5.4 总结一下取消注册的过程
一、首先获取订阅者的全部订阅事件
二、遍历订阅事件
三、根据订阅事件获取全部的订阅了该事件的订阅者集合
四、将该订阅者移除
五、将步骤1中的集合中的订阅者移除
6.总结一下EventBus的工做原理
6.1 订阅逻辑
一、首先用register()方法注册一个订阅者
二、获取该订阅者的全部订阅的方法
三、根据该订阅者的全部订阅的事件类型,将订阅者存入到每一个以 事件类型为key 以全部订阅者为values的map集合中
四、而后将订阅事件添加到以订阅者为key 以订阅者全部订阅事件为values的map集合中
五、若是是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取以前发送过的粘滞事件,响应这些粘滞事件。
6.2 事件发送逻辑
一、首先获取当前线程的事件队列
二、将要发送的事件添加到事件队列中
三、根据发送事件类型获取全部的订阅者
四、根据响应方法的执行模式,在相应线程经过反射执行订阅者的订阅方法
6.3 取消逻辑
一、首先经过unregister方法拿到要取消的订阅者
二、获得该订阅者的全部订阅事件类型
三、遍历事件类型,根据每一个事件类型获取到全部的订阅者集合,并从集合中删除该订阅者
四、将订阅者从步骤2的集合中移除
6.4 利与弊
EventBus好处比较明显,它可以解耦和,将业务和视图分离,代码实现比较容易。并且3.0后,咱们能够经过apt预编译找到订阅者,避免了运行期间的反射处理解析,大大提升了效率。固然EventBus也会带来一些隐患和弊端,若是滥用的话会致使逻辑的分散并形成维护起来的困难。另外大量采用EventBus代码的可读性也会变差。
7.其余介绍
7.1 参考文档
EventBus源码,能够看:https://github.com/greenrobot/EventBus
EventBus源码解析:https://www.cnblogs.com/all88/archive/2016/03/30/5338412.html
EventBus用法解析:http://blog.csdn.net/itachi85/article/details/52205464
7.2 其余
脉脉:yc930211
泡在网上的日子:http://www.jcodecraeer.com/me...
邮箱:yangchong211@163.com