EventBus详解

来源:互联网 发布:装修公司半包猫腻知乎 编辑:程序博客网 时间:2024/04/30 02:21

EventBus详解

1. 功能介绍

1.1 EventBus


    EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。
    事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,
    相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。

1.2 相关概念

    事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属
    的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近
    一个 Sticky 事件。
    订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。订阅者通过 register
    接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
    发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。

1.3 订阅者、发布者、EventBus 关系图



1.4 EventBus事件响应的流程


2.EventBus的使用


EventBus.getDefault().register(this);//订阅事件

EventBus.getDefault().post(object);//发布事件

EventBus.getDefault().unregister(this);//取消订阅


大多情况下,都会在onCreate中进行register,在onDestory中进行unregister ;


在onCreate注册EventBus:  EventBus.getDefault().register(this);


意思是让EventBus扫描当前类,把所有onEvent开头的方法记录下来,如何记录呢?使用Map,Key为方法的参数类型,Value中包含我们的方法。

这样在onCreate执行完成以后,我们的onEventMainThread就已经以键值对的方式被存储到EventBus中了。

然后当子线程执行完毕,调用EventBus.getDefault().post(new ItemListEvent(Item.ITEMS))时,EventBus会根据post中实参的类型,去Map中查找对于的方法,于是找到了我们的onEventMainThread,最终调用反射去执行我们的方法


在onDestory中反注册    :  EventBus.getDefault().unregister(this);

定义方法onEventMainThread(VideoInfo info)

如果方法名以onEvent开头,则代表要订阅一个事件,MainThread意思,这个方法最终要在UI线程执行;当事件发布的时候,这个方法就会被执行。

 Activity端

        public void OnEventMainThread(VideoInfo info) {
            
        //根据接受的信息做一些事情,如果只需要知道对方的一些状态,不适用对方的发过来的信息也是可以使双方联系起来的
        //如:seekbarAudio.setMax((int) mediaItem.getDuration());
        //info为service(Activity等通信的另一端)传过来的数据对象
            }
 
* 服务端 --- 服务端发送数据,OnEventMainThread(VideoInfo info)接收数据,类型一致

        EventBus.getDefault().post(info);//将当前播放的对象传过去


3.EventBus的四种模式

EventBus包含4个ThreadMode:PostThread,MainThread,BackgroundThread,Async

MainThread我们已经不陌生了;我们已经使用过。

具体的用法,极其简单,方法名为:onEventPostThread, onEventMainThread,onEventBackgroundThread,onEventAsync即可

具体什么区别呢?

 PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;

MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;

BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;

Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。

EventBus是如何区分这四种模式的呢?

其实在EventBus内部使用了Map进行存储,键就是参数的Class类型。当你进行post的时候,根据post传入的参数,去找到匹配的方法,反射进行调用


4.源码解析


  • EventBus.java

EventBus 类负责所有对外暴露的 API,其中的 register()、post()、unregister() 函数配合上自定义的 EventType 及事件响应函数即可完成核心功能

对于变量的说明:

1.defaultInstance默认的 EventBus 实例,根据EventBus.getDefault()函数得到。
2.DEFAULT_BUILDER默认的 EventBus Builder。
3.eventTypesCache事件对应类型及其父类和实现的接口的缓存,以 eventType 为 key,元素为 Object 的 ArrayList 为 Value,Object 对象为 eventType 的父类或接口。 4.subscriptionsByEventType事件订阅者的保存队列,以 eventType 为 key,元素为Subscription的 ArrayList 为 Value,其中Subscription为订阅者信息,由 subscriber, subscriberMethod, priority 构成。
5.typesBySubscriber订阅者订阅的事件的保存队列,以 subscriber 为 key,元素为 eventType 的 ArrayList 为 Value。
6.stickyEventsSticky 事件保存队列,以 eventType 为 key,event 为元素,由此可以看出对于同一个 eventType 最多只会有一个 event 存在。
7.currentPostingThreadState当前线程的 post 信息,包括事件队列、是否正在分发中、是否在主线程、订阅者信息、事件实例、是否取消。
8.mainThreadPosterbackgroundPosterasyncPoster事件主线程处理者、事件 Background 处理者、事件异步处理者。
9.subscriberMethodFinder订阅者响应函数信息存储和查找类。
10.executorService异步和 BackGround 处理方式的线程池。
11.throwSubscriberException当调用事件处理函数异常时是否抛出异常,默认为 false,建议通过
EventBus.builder().throwSubscriberException(true).installDefaultEventBus()
打开。
12.logSubscriberExceptions当调用事件处理函数异常时是否打印异常信息,默认为 true。
13.logNoSubscriberMessages当没有订阅者订阅该事件时是否打印日志,默认为 true。
14.sendSubscriberExceptionEvent当调用事件处理函数异常时是否发送 SubscriberExceptionEvent 事件,若此开关打开,订阅者可通过
public void onEvent(SubscriberExceptionEvent event)
订阅该事件进行处理,默认为 true。
15.sendNoSubscriberEvent当没有事件处理函数对事件处理时是否发送 NoSubscriberEvent 事件,若此开关打开,订阅者可通过
public void onEvent(NoSubscriberEvent event)
订阅该事件进行处理,默认为 true。
16.eventInheritance是否支持事件继承,默认为 true。

EventBus 默认可通过静态函数 getDefault 获取单例,当然有需要也可以通过 EventBusBuilder 或 构造函数新建一个 EventBus,每个新建的 EventBus 发布和订阅事件都是相互隔离的,即一个 EventBus 对象中的发布者发布事件,另一个 EventBus 对象中的订阅者不会收到该订阅。

  public static EventBus getDefault() {        if (defaultInstance == null) {            synchronized (EventBus.class) {                if (defaultInstance == null) {                    defaultInstance = new EventBus();                }            }        }        return defaultInstance;    }    public static EventBusBuilder builder() {        return new EventBusBuilder();    }


EventBus 中对外 API,主要包括以下3类:
(1) register 和 unregister

分别表示订阅事件和取消订阅。register 最底层函数有三个参数,分别为订阅者对象、是否是 Sticky 事件、优先级。

  /**     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they     * are no longer interested in receiving events.     * <p/>     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.     * The {@link Subscribe} annotation also allows configuration like {@link     * ThreadMode} and priority.     */    public void register(Object subscriber) {        Class<?> subscriberClass = subscriber.getClass();        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);        synchronized (this) {            for (SubscriberMethod subscriberMethod : subscriberMethods) {                subscribe(subscriber, subscriberMethod);            }        }    }

在register 函数中会先根据订阅者类名去subscriberMethodFinder中查找当前订阅者所有事件响应函数,然后循环每一个事件响应函数,依次执行下面的 subscribe 函数


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) {                unsubscribeByEventType(subscriber, eventType);            }            typesBySubscriber.remove(subscriber);        } else {            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());        }    }

(2) subscribe
subscribe 函数分三步


    // Must be called in synchronized block    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {        //第一步:通过subscriptionsByEventType得到该事件类型所有订阅者信息队列,        // 根据优先级将当前订阅者信息插入到订阅者队列subscriptionsByEventType中;        Class<?> eventType = subscriberMethod.eventType;        /**         * 这里的subscriptionsByEventType是个Map,key:eventType ; value:CopyOnWriteArrayList<Subscription> ;         * 这个Map其实就是EventBus存储方法的地方,一定要记住!         */        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);        if (subscriptions == null) {            subscriptions = new CopyOnWriteArrayList<>();            subscriptionsByEventType.put(eventType, subscriptions);        } else {            if (subscriptions.contains(newSubscription)) {                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "                        + eventType);            }        }        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中得到当前订阅者订阅的所有事件队列,将此事件保存到队列typesBySubscriber中,用于后续取消订阅;        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);        if (subscribedEvents == null) {            subscribedEvents = new ArrayList<>();            typesBySubscriber.put(subscriber, subscribedEvents);        }        subscribedEvents.add(eventType);        /**         *第三步:检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。         * 判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行。         * stickyEvent其实就是我们post时的参数。         */        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);            }        }    }

附 :checkPostStickyEventToSubscription的实现代码

    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {        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());        }    }


附 :postToSubscription的实现代码


  private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {        switch (subscription.subscriberMethod.threadMode) {//<span style="color:#FF0000;">根据事件的模式去执行相应的反射调用</span>            case POSTING:                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);        }    }



(3) post、cancel 、removeStickyEvent

  post 函数用于发布事件,cancel 函数用于取消某订阅者订阅的所有事件类型、removeStickyEvent 函数用于删除 sticky 事件。

   A.post

post 函数流程图如下:
post 函数会首先得到当前线程的 post 信息PostingThreadState,其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。

/** * post 函数会首先得到当前线程的 post 信息PostingThreadState, * 其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。 * Posts the given event to the event bus. */public void post(Object event) {    /**     * currentPostingThreadState是一个ThreadLocal类型的,里面存储了PostingThreadState;     * PostingThreadState包含了一个eventQueue和一些标志位。     */    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;        }    }}

postSingleEvent 函数会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 函数发布每个事件到每个订阅者。


private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {    Class<?> eventClass = event.getClass();    boolean subscriptionFound = false;    /**     * 根据event的Class,去得到一个List<Class<?>>;其实就是得到event当前对象的Class,     * 以及父类和接口的Class类型;主要用于匹配,比如你传入Dog extends Dog,他会把Animal也装到该List中。     */    if (eventInheritance) {        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);        int countTypes = eventTypes.size();        //遍历所有的Class,到subscriptionsByEventType去查找subscriptions        for (int h = 0; h < countTypes; h++) {            Class<?> clazz = eventTypes.get(h);            //postSingleEventForEventType 函数在subscriptionsByEventType查找该事件订阅者订阅者队列,            // 调用 postToSubscription 函数向每个订阅者发布事件。            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));        }    }}



postSingleEventForEventType 函数在subscriptionsByEventType查找该事件订阅者订阅者队列,调用 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;    }

  • EventBusBuilder.java

跟一般 Builder 类似,用于在需要设置参数过多时构造 EventBus。包含的属性也是 EventBus 的一些设置参数,build 函数用于新建 EventBus 对象,installDefaultEventBus 函数将当前设置应用于 Default EventBus。

该类提供了以下几个方法:



这里只介绍其中两个方法:

/** * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified). * Switching this feature off will improve posting of events. For simple event classes extending Object directly, * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be * >20%. * However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app, * unless it is posting at high rates, e.g. hundreds/thousands of events per second. */public EventBusBuilder eventInheritance(boolean eventInheritance) {    this.eventInheritance = eventInheritance;    return this;}

这段英文大意是是:默认的情况下事件总线的事件类是有层次的(事件订阅者的父类也会被通知)。关闭这个特性将会提高事件提交的速度。我们通过测量得知,一个直接继承子Object的简单的事件类的提交速度为20%,一个复杂事件类事件提交的速度大于20%。然而,记住,对于App来说,提交事件通常只需要耗费一小部分的Cpu时间除非提交一个高消耗的时间例如:每秒要执行百或者千次的时间。

 所以只要不是频率较高的事件类,事件提交的耗费是很小的。

/** * Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced * setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior. */public EventBusBuilder executorService(ExecutorService executorService) {    this.executorService = executorService;    return this;}

  • SubscriberMethodFinder.java

订阅者响应函数信息存储和查找类,由 HashMap 缓存,以 ${subscriberClassName} 为 key,SubscriberMethod 对象为元素的 ArrayList 为 value。findSubscriberMethods 函数用于查找订阅者响应函数,如果不在缓存中,则遍历自己的每个函数并递归父类查找,查找成功后保存到缓存中。遍历及查找规则为:
  a. 遍历 subscriberClass 每个方法;
  b. 该方法不以java.、javax.、android.这些 SDK 函数开头,并以onEvent开头,表示可能是事件响应函数继续,否则检查下一个方法;
  c. 该方法是否是 public 的,并且不是 ABSTRACT、STATIC、BRIDGE、SYNTHETIC 修饰的,满足条件则继续。其中 BRIDGE、SYNTHETIC 为编译器生成的一些函数修饰符;
  d. 该方法是否只有 1 个参数,满足条件则继续;
  e. 该方法名为 onEvent 则 threadMode 为ThreadMode.PostThread;
该方法名为 onEventMainThread 则 threadMode 为ThreadMode.MainThread;
该方法名为 onEventBackgroundThread 则 threadMode 为ThreadMode.BackgroundThread;
该方法名为 onEventAsync 则 threadMode 为ThreadMode.Async;
其他情况且不在忽略名单 (skipMethodVerificationForClasses) 中则抛出异常。
  f. 得到该方法唯一的参数即事件类型 eventType,将这个方法、threadMode、eventType 一起构造 SubscriberMethod 对象放到 ArrayList 中。
  g. 回到 b 遍历 subscriberClass 的下一个方法,若方法遍历结束到 h;
  h. 回到 a 遍历自己的父类,若父类遍历结束回到 i;
  i. 若 ArrayList 依然为空则抛出异常,否则会将 ArrayList 做为 value,${subscriberClassName} 做为 key 放到缓存 HashMap 中。 对于事件函数的查找有两个小的性能优化点:
a. 第一次查找后保存到了缓存中,即上面介绍的 HashMap  b. 遇到 java. javax. android. 开头的类会自动停止查找
类中的 skipMethodVerificationForClasses 属性表示跳过哪些类中非法以 onEvent 开头的函数检查,若不跳过则会抛出异常。
PS:在此之前的版本 EventBus 允许自定义事件响应函数名称,缓存的 HashMap key 为 ${subscriberClassName}.${eventMethodName},这版本中此功能已经被去除。

  • SubscriberMethod.java

订阅者事件响应函数信息,包括响应方法、线程 Mode、事件类型以及一个用来比较 SubscriberMethod 是否相等的特征值 methodString 共四个变量,其中 methodString 为 ${methodClassName}#${methodName}(${eventTypeClassName}。

  • Subscription.java

订阅者信息,包括 subscriber 对象、事件响应方法 SubscriberMethod、优先级 priority。

  • HandlerPoster.jva

事件主线程处理,对应ThreadMode.MainThread。继承自 Handler,enqueue 函数将事件放到队列中,并利用 handler 发送 message,handleMessage 函数从队列中取事件,invoke 事件响应函数处理。

  •  AsyncPoster.java

事件异步线程处理,对应ThreadMode.Async,继承自 Runnable。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。

  • BackgroundPoster.java

事件 Background 处理,对应ThreadMode.BackgroundThread,继承自 Runnable。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。与 AsyncPoster.java 不同的是,BackgroundPoster 中的任务只在同一个线程中依次执行,而不是并发执行。

  • PendingPost.java

订阅者和事件信息实体类,并含有同一队列中指向下一个对象的指针。通过缓存存储不用的对象,减少下次创建的性能消耗。
4.2.10 PendingPostQueue.java
通过 head 和 tail 指针维护一个PendingPost队列。HandlerPoster、AsyncPoster、BackgroundPoster 都包含一个此队列实例,表示各自的订阅者及事件信息队列,在事件到来时进入队列,处理时从队列中取出一个元素进行处理。

  • SubscriberExceptionEvent.java

当调用事件处理函数异常时发送的 EventBus 内部自定义事件,通过 post 发送,订阅者可自行订阅这类事件进行处理。

  • NoSubscriberEvent.java

当没有事件处理函数对事件处理时发送的 EventBus 内部自定义事件,通过 post 发送,订阅者可自行订阅这类事件进行处理。

  • EventBusException.java

封装于 RuntimeException 之上的 Exception,只是覆盖构造函数,相当于一个标记,标记是属于 EventBus 的 Exception。

  •  ThreadMode.java

线程 Mode 枚举类,表示事件响应函数执行线程信息,包括ThreadMode.PostThread、ThreadMode.MainThread、ThreadMode.BackgroundThread、ThreadMode.Async四种。



参考了大牛的博客,目前正在学习中,有不对的见解,欢迎指正,谢谢

0 0
原创粉丝点击