EventBus 3.0源码剖析
简介
EvenntBus 是一个开源库,它利用公布/订阅者者模式来对我的项目进行解耦。它能够利用很少的代码,来实现多组件间通信。android的组件间通信,咱们不由得会想到handler音讯机制和播送机制,通过它们也能够进行通信,然而应用它们进行通信,代码量多,组件间容易产生耦合援用。对于EventBus的工作模式,这里援用一张官网图帮忙了解。
为什么会抉择应用EventBus来做通信?
- 简化了组件间交换的形式
- 对事件通信单方进行解耦
- 能够灵便不便的指定工作线程,通过ThreadMode
- 速度快,性能好
- 库比拟小,60k左右,对包大小无影响
- 应用这个库的app多,有权威性
- 性能多,使用方便
EventBus的应用也非常简单,三板斧。register,unregister, subscribe/post
三个重要的角色
1:Publisher 事件发布者
2:Subscriber 事件订阅者
3:Event 事件
Publisher post 事件后,Subscriber会主动收到事件(订阅办法会被被动调用,并将事件传递过去)。
应用
//倒入gradle 依赖 implementation 'org.greenrobot:eventbus:3.3.1'
1:定义事件类型
public static class MessageEvent { /* Additional fields if needed */ }
2:在须要订阅事件的模块中注册EventBus,页面销毁时留神登记
@Override protected void onStart() { super.onStart(); EventBus.getDefault().register(this); }@Overrideprotected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this);}
3:注册须要承受的事件类型 //留神同一种事件类型不能反复注册。不然会解体,且订阅办法必须是public类型的。
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { // Do something}
4.发送事件
EventBus.getDefault().post(new MessageEvent());这是步骤3种的办法就会收到MessageEvent事件的回调
源码剖析
EventBus 主类中只有不到600行代码,十分精简。EventBus应用了对外提供了单例模型,外部构建应用了Build模式。
1 register
public void register(Object subscriber) { if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) { // Crash if the user (developer) has not imported the Android compatibility library. throw new RuntimeException("It looks like you are using EventBus on Android, " + "make sure to add the \"eventbus\" Android library to your dependencies."); } //1从这里开始看,获取调用者的类对象。 Class<?> subscriberClass = subscriber.getClass(); //2 找到订阅类中的订阅办法 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //3遍历订阅办法,将订阅者和其中的订阅办法绑定。 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } }}
Step1
看下步骤2中的subscriberMethodFinder的findSubscriberMethods办法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //1首先从缓存map中拿到订阅类的订阅办法列表,应用了缓存进步性能,nice,不出所料METHOD_CACHE的类型是Map<Class<?>, List<SubscriberMethod> > List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); //2 如果不为空,阐明之前该类已经注册过,该类的新对象不用从新做绑定了,因为此时的操作是类层面的 if (subscriberMethods != null) { return subscriberMethods; } //如果subscriberMethods 为null,阐明该类是第一次注册,须要将其中的接管办法保存起来, //ignoreGeneratedIndex 默认为false if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else { subscriberMethods = findUsingInfo(subscriberClass); } //如果subscriberMethods为null,阐明以后类对象没有生命订阅办法,抛出异样 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; }}
Step2
从步骤2中找出类的注册办法列表,而后遍历列表,调用上面的办法,将类对象和注册办法绑定。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //1 找到订阅办法的事件类型,即发送事件的MessageEvent.class Class<?> eventType = subscriberMethod.eventType; //2 将订阅者类对象和订阅事件绑定成一个对象 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //subscriptionsByEventType 这个汇合必定是用来搁置同一事件类型的订阅汇合的,因为一个事件可能会有多个订阅的。 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { //如果一个订阅者屡次订阅了一个事件(@Subscribe注解的办法的参数是同一类型),抛出异样 if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //3 依照订阅办法中@Subscribe中的priority参数进行排序,默认为最低优先级0。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; } } // typesBySubscriber类型Map<Object, List<Class<?>>>,Key 为订阅者,value为订阅者中的订阅办法,用来记录每个订阅者外部都订阅了哪些事件类型 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); //粘性事件相干 if (subscriberMethod.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); } }}
2 post
/** Posts the given event to the event bus. */public void post(Object event) { //1首先获取以后线程的工作状态 PostingThreadState postingState = currentPostingThreadState.get(); //2获取以后线程的工作队列 List<Object> eventQueue = postingState.eventQueue; //3 将事件退出到事件队列 eventQueue.add(event); //4 如果以后线程的工作状态没有正在发送事件 if (!postingState.isPosting) { //标记postingState 的是否是主线程,并将工作状态isPosting 设为true postingState.isMainThread = isMainThread(); 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; } }}/*** 发送事件* @param event 事件* @param postingState 以后线程相干的配置*/private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //如果应用继承事件的父类/接口,比方你发送了MessageEvent 事件,如果该事件继承了BaseEvent和Ievent接口,那么当你发送 MessageEvent 事件时,零碎也会发送BaseEvent和Ieven事件 if (eventInheritance) { //遍历父类,将事件的父类/接口通通退出到eventTypes中 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); //遍历eventTypes,顺次发送调用事件 subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); } } else { //不实用事件继承模型,间接发送该事件 subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); } if (!subscriptionFound) { //如果该事件没有订阅者抛出异样 if (logNoSubscriberMessages) { logger.log(Level.FINE, "No subscribers registered for event " + eventClass); } //EventBus 外部也实用EventBus 发送了一个异样事件 if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { //1获取该事件的所有订阅关系列表 subscriptions = subscriptionsByEventType.get(eventClass); } //2 遍历订阅关系列表,顺次将事件发送到订阅者 if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postingState.event = event; postingState.subscription = subscription; boolean aborted; 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) { //1 首先判断订阅关系中订阅办法的线程,就是申明线程时应用@Subcribe注解时传入的threadMode字段的值 switch (subscription.subscriberMethod.threadMode) { case POSTING: //间接发送事件 invokeSubscriber(subscription, event); break; case MAIN: //在主线程相应事件 //事件收回线程是否是主线程 if (isMainThread) { 是,间接发送 invokeSubscriber(subscription, event); } else {不是通过mainThreadPoster发送 mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(subscription, event); } break; case BACKGROUND: //在后盾线程相应事件 //事件收回线程是否是主线程 if (isMainThread) {主线程发送事件,backgroundPoster转发 backgroundPoster.enqueue(subscription, event); } else { 非主线程发送事件,间接发送 invokeSubscriber(subscription, event); } break; case ASYNC: //异步线程相应事件 //通过asyncPoster发送事件 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,backgroundPoster,asyncPoster
其中mainThreadPoster的类型是HandlerPoster。其实就是Handler。调用其enqueu()办法
而backgroundPoster和asyncPoster 实质都是Runnable
3 unregister
解绑办法就简略多了,重要的是就把register里提到的2个重要的几何中删除订阅者
/** Unregisters the given subscriber from all event classes. */public synchronized void unregister(Object subscriber) { //1 从typesBySubscriber找到订阅者所订阅的事件类型列表 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { //2 遍历列表,顺次解绑订阅者和事件类型。应该是从post剖析里的订阅事件汇合subscriptionsByEventType里移除对应事件类型的该订阅者 for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } //3移除订阅者 typesBySubscriber.remove(subscriber); } else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); }}/*** 解绑订阅者和事件类型* @param subscriber 订阅者* @param eventType 订阅的事件类型*/ private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { //从subscriptionsByEventType里获取该订阅事件的订阅者汇合。 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); //重要。不然会抛出ConcurrentModifyException i--; size--; } } } }
4 Subscribe注解
@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POSTING; /** * If true, delivers the most recent sticky event (posted with * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). */ boolean sticky() default false; /** Subscriber priority to influence the order of event delivery. * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of * delivery among subscribers with different {@link ThreadMode}s! */ int priority() default 0;}
5 ThreadMode
//定义事件回调办法工作线程的类public enum ThreadMode { /** *间接在发送事件的线程里调用Subscriber,这个是默认的设置,事件交付开销最小,因为它防止了线程切换。因而它是那种很快实现的单任务 *默认的的线程工作模型。应用该模型的事件必须很快实现,因为当公布线程是主线程时,它可能阻塞主线程。 / POSTING, /** * 在Android平台,订阅者将会在Android的主线程调用。如果公布线程时主线程,订阅办法将会被间接调用。进而阻赛公布线程,如果公布线 * 程不是主线程。事件将会排队期待散发。应用这种模式的订阅者必须疾速实现工作,防止阻赛主线程。非Android平台和Posting一样 */ MAIN, /** * 在Android平台,订阅者将会在Android的主线程调用。不同于MAIN,事件将会有序散发。确保了post调用时非阻赛的。 */ MAIN_ORDERED, /** * 在Android平台,订阅者将会在后盾线程被调用,如果公布线程不是主线程,订阅者将会被间接调用,如果公布线程时主线程,那么EventBus * 应用后盾线程,进而有序散发所有事件,应用此模式的Subscribers应该疾速实现工作免得阻赛后台线程。非Android平台,总是用后盾线程 * 相应事件 */ BACKGROUND, /** * 订阅者将会在独自的线程被调用,总是独立于公布线程和主线程。公布事件从不会期待应用这种模式的订阅办法。如果订阅办法执行耗时工作, * 则应该应用此模式。比方;网络拜访。防止同时触发大量的长时间运行的异步订阅办法,从而限度并发的线程数量。EventBus 应用线程池来 * 高效的服用已实现异步订阅告诉的线程 */ ASYNC}
6 EventBus2.0和EventBus3.0的区别?
这是在面试过程中,面试官最常问的一个问题。
EventBus2.0和3.0最大的区别有两点:
1.EventBus2.0中咱们在书写订阅办法时的名字必须是onEvent结尾,而后通过命名不同来区别不同的线程模式。例如对应posting则命名为onEvent(),onEventMainThread()则对应main等。而3.0则能够用任何名字作为办法名称,只须要在办法名的后面用@Subscribe注解来进行正文,而后应用threadMode来设置在哪里线程中接管事件和处理事件
2.EventBus2.0应用的是反射的形式来查找所有的订阅办法,而3.0则是在编译时通过注解处理器的形式来查找所有的订阅办法。性能上来说,3.0比2.0要高的多。