EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)

来源:互联网 发布:吉林市退休工资算法 编辑:程序博客网 时间:2024/06/18 13:44

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维护了两个重要的map:

  1. 事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
  2. 订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。

因此register注册订阅者的时候,EventBus会通过反射寻找订阅者订阅的所有事件,更新上述两个map。如果是粘性sticky事件,在订阅的时候就取出保存的sticky事件直接发送,这样就做到了发布者先发布事件,之后订阅者订阅事件,接收订阅之前发布的粘性事件。

    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);        }    }

可以看到register主要做了两件事:

  1. 找到订阅者订阅的所有事件
  2. 对每个事件分别订阅,更新map

通过反射找到订阅者订阅的所有事件

在register方法中,要更新两个map,必须首先获取订阅者订阅的所有事件。

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) 

findSubscriberMethods方法传入订阅者的Class对象,返回该订阅者订阅的所有事件处理方法列表。
该方法代码比较长,分段进行阅读。

        ...        private static final Map<String, List<SubscriberMethod>> methodCache =         new HashMap<String, List<SubscriberMethod>>();        ...        String key = subscriberClass.getName();        List<SubscriberMethod> subscriberMethods;        synchronized (methodCache) {            subscriberMethods = methodCache.get(key);        }        if (subscriberMethods != null) {            return subscriberMethods;        }

因为反射查找事件处理方法比较耗费性能,因此我们加入缓存机制,通过静态HashMap保存订阅者的事件处理方法。
先查找缓存,缓存命中的话直接返回事件处理方法列表,否则通过反射重新查找获取。

     while (clazz != null) {         ...         clazz = clazz.getSuperclass();     }

clazz.getSuperclass()方法会返回clazz父类的Class对象,如果clazz代表的类是根类Object、原生类型、接口或者Void则该方法返回null,因此EventBus不仅会保存当前订阅者订阅的事件,还会保存订阅者的父类订阅的事件。

            String name = clazz.getName();            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {                // Skip system classes, this just degrades performance                break;            }

为了性能考虑,直接跳过包名为java、javax和androd开头的类,因为这些类是系统类,我们不会订阅系统类为订阅者,因此不需处理系统类。

   // 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();                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {                    int modifiers = method.getModifiers();                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {                        Class<?>[] parameterTypes = method.getParameterTypes();                        if (parameterTypes.length == 1) {                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());                            ThreadMode threadMode;                            if (modifierString.length() == 0) {                                threadMode = ThreadMode.PostThread;                            } else if (modifierString.equals("MainThread")) {                                threadMode = ThreadMode.MainThread;                            } else if (modifierString.equals("BackgroundThread")) {                                threadMode = ThreadMode.BackgroundThread;                            } else if (modifierString.equals("Async")) {                                threadMode = ThreadMode.Async;                            } else {                                if (skipMethodVerificationForClasses.containsKey(clazz)) {                                    continue;                                } else {                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);                                }                            }

接下来通过EventBus约定的规则去寻找事件处理方法
事件处理方法必须是onEvent开头

    private static final String ON_EVENT_METHOD_NAME = "onEvent";    ...    if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {    ...

只能是public的非抽象(abstract)、非静态(static)方法

    int modifiers = method.getModifiers();    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {    ...

事件处理方法只能有一个参数

    Class<?>[] parameterTypes = method.getParameterTypes();    if (parameterTypes.length == 1) {    ...

经过上述重重过滤,找到了事件处理方法,然后根据事件处理方法的命名规则,判定处理该事件的线程模型

     String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());     ThreadMode threadMode;     if (modifierString.length() == 0) {         threadMode = ThreadMode.PostThread;     } else if (modifierString.equals("MainThread")) {         threadMode = ThreadMode.MainThread;     } else if (modifierString.equals("BackgroundThread")) {         threadMode = ThreadMode.BackgroundThread;     } else if (modifierString.equals("Async")) {         threadMode = ThreadMode.Async;     }

由此可知:

  1. onEvent方法的线程模型是PostThread,在发布者线程处理事件
  2. onEventMainThread方法的线程模型是MainThread,在主线程处理事件
  3. onEventBackgroundThread方法的线程模型是BackgroundThread
  4. onEventAsync方法的线程模型是Async

BackgroundThread和Async线程模型的区别随后会专门研究讨论,此处不表。

       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));       }

接着以”事件处理方法名>事件类型“为key,加入到一个set中

        HashSet<String> eventTypesFound = new HashSet<String>();

eventTypesFound这个set的主要作用是去重,如果子类覆写了父类的事件处理方法,那么EventBus只保存子类的事件处理方法,父类的事件处理方法就像覆写的意义那样,被覆盖掉。

     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;     }

如果订阅者没有订阅事件,也就是说没有找到事件处理方法,那么抛出异常。
如果找到了该订阅者的所有事件处理方法,在返回之前,要添加缓存。

对每个事件进行单独订阅,更新两个map

找到订阅者订阅的所有事件后,会通过subscribe方法对每个事件进行单独订阅。

      for (SubscriberMethod subscriberMethod : subscriberMethods) {          subscribe(subscriber, subscriberMethod, sticky, priority);      }

subscribe方法实际上是更新两个map:

  1. 事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
  2. 订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。
    // Must be called in synchronized block    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {        Class<?> eventType = subscriberMethod.eventType;        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());            }        }    }

首先根据事件从map中获取该事件的订阅者列表,根据参数创建一个Subscription对象(封装了订阅者,事件处理方法和优先级)

     Class<?> eventType = subscriberMethod.eventType;     CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);     Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);

如果该事件没有订阅者列表的话,就新建一个订阅者列表添加进map

        if (subscriptions == null) {            subscriptions = new CopyOnWriteArrayList<Subscription>();            subscriptionsByEventType.put(eventType, subscriptions);        }

否则判断是否为重复订阅,是的话抛出异常。
接着根据新的订阅者的优先级将新订阅者插入订阅者列表的合适位置

        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;            }        }

可见优先级数值越大,越靠近列表的前部。
因此订阅者列表是按照优先级从大到小排队的。
接着更新另外一个map,获取该订阅者订阅的所有事件

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);        if (subscribedEvents == null) {            subscribedEvents = new ArrayList<Class<?>>();            typesBySubscriber.put(subscriber, subscribedEvents);        }        subscribedEvents.add(eventType);

最后判断是否为sticky事件,是的话就读取保存的sticky事件发布出去

        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());            }        }

事件的发布将在下一篇中继续分析。

0 0