美文网首页
EventBus的理解

EventBus的理解

作者: 北雁南飞_8854 | 来源:发表于2018-04-15 17:08 被阅读0次

一、Event的注册

使用方式:

EventBus.getDefault().register(this);

所做的事情:

  1. 保存当前订阅类所订阅的所有事件类型,保存每种事件类型对应的Subscription列表。
    //Key为订阅类,Value为事件类型列表。
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    //Key为事件类型的Class实例,Value为订阅该事件的所有Subscription列表。
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  1. 使用Subscription类封装订阅者(Object)和订阅方法(SubscriberMethod),将该Subscription对象插入到当前事件类型对应的CopyOnWriteArrayList<Subscription>中,并且按照subscriberMethod.priority由高到低排序;
  2. 将当前事件类型插入到当前订阅类的事件类型列表中。
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
image.png

二、查找订阅类的所有订阅方法

1. 查找方法

通过反射查找订阅方法,需要满足以下条件:

  • public且非static、非abstract的方法;
  • 只有一个参数;
  • 注解类型为Subscribe;

2. 订阅方法的保存

使用SubscriberMethod类保存,设计为:

public class SubscriberMethod {
    final Method method; //订阅方法的Method实例
    final ThreadMode threadMode; //线程模式
    final Class<?> eventType; //传递的消息类型,也即订阅方法的参数类型;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
    ...
}

3. 查找状态的保存

使用FindState类,设计为:

static class FindState {
    //订阅方法列表
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    //订阅方法的参数类型Class<?> --> 订阅方法Method实例;
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();

    //订阅方法的函数签名:<方法名称> + ">" + <参数类型名> --> 订阅类的Class实例.
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    Class<?> subscriberClass;
    Class<?> clazz;
    boolean skipSuperClasses;
    SubscriberInfo subscriberInfo;

    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }

    void recycle();
    //添加
    boolean checkAdd(Method method, Class<?> eventType);
    private boolean checkAddWithMethodSignature(Method method, Class<?> eventType);
    void moveToSuperclass();
}
class SubscriberMethodFinder { 
   private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                ...
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
   
    /**
     * 通过反射查找订阅方法,需要满足以下条件:
     * 1. public且非static、非abstract的方法;
     * 2. 只有一个参数;
     * 3. 注解类型为Subscribe;
     * @param findState
     */
    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }
}

4. 查找和存储过程:

(1)查第1个HashMap:

final Map<Class, Object> anyMethodByEventType;

其中,Key为EventType1对象的Class实例;Value为订阅方法的Method实例。
(2)若(1)中已存在相同的Key值,则查第2个HashMap:

final Map<String, Class> subscriberClassByMethodKey;

其中,Key为订阅方法的签名信息,格式:

<methodName> + “>” + <eventType1ClassName>

Value为订阅方法所在类的Class实例。

分几种情况来考虑

(1)当前类有两个及以上不同名的方法订阅EventType1:
结果:
所有不同的订阅方法都有效,均保存在第2个的HashMap中;
(2)当前类有方法订阅EventType1,父类也有同名的方法订阅EventType1:
结果:
父类的同名订阅方法被覆盖,无效;使用当前类的同名方法。
(3)当前类有方法订阅EventType1,父类也有不同名的方法订阅EventType1:
结果:
父类和当前类的订阅方法都有效。
下面详细分析一下第(1)种情况的实现流程:

  • 找到第1个订阅的方法,插入Key-Value对 {Class<EventType1>, 方法1的Method实例} 到第1个HashMap中,checkAdd()方法返回true,表示插入成功;
  • 找到第2个订阅方法,插入Key-Value对 {Class<EventType1>,方法2的Method实例},因为已经存在相同的Key值,所以开始使用第2个HashMap。
    ① 取出第1个HashMap的Key为Class<EventType1>的Value。
    --> 若是Method实例:
    将该Method实例是第1个订阅方法插入的,先将其插入到第2个HashMap中;若插入失败,抛出IllegalStateException异常;若插入成功,则进入步骤2;
    --> 若不是Method实例,直接进入步骤3;
    ② 插入{Class<EventType>, 非Method实例},将原来的 {Class<EventType>,Method实例} 破坏掉;
    ③ 将第2个订阅方法插入到第2个HashMap中。
    完整的源码如下:
static class FindState {
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();

        //<methodName> + ">" + <eventTypeName> -> method所在的类的Class.
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;

         void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

        void recycle() {
            subscriberMethods.clear();
            anyMethodByEventType.clear();
            subscriberClassByMethodKey.clear();
            methodKeyBuilder.setLength(0);
            subscriberClass = null;
            clazz = null;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

        boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            } else {
                if (existing instanceof Method) {
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

        void moveToSuperclass() {
            if (skipSuperClasses) {
                clazz = null;
            } else {
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                /** Skip system classes, this just degrades performance. */
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    clazz = null;
                }
            }
        }
    }

三、消息的发布和订阅

发布的状态使用PostingThreadState描述,

final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<Object>(); //等待处理的事件
    boolean isPosting;
    boolean isMainThread; //post()操作是否位于主线程
    Subscription subscription;
    Object event;
    boolean canceled;
}
/**
 * 查找HashMap,获取该事件类型对应的Subscriptions列表。
 * 结束后PostingThreadState的event和subscription置为null, canceled置为false.
 */
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;
}

四、消息传递的拦截

仅限于ThreadMode.POSTING模式,在处理事件的方法中调用,后面的事件传送会被取消,即后面的调阅方法将收不到该事件。

public void cancelEventDelivery(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    //注意:只有当postingState.isPosting为true,即在post的过程中(已开始post,订阅者们还未全部收到)才有效。
    if (!postingState.isPosting) {
        throw new EventBusException(
                    "This method may only be called from inside event handling methods on the posting thread");
    } else if (event == null) {
        throw new EventBusException("Event may not be null");
    } else if (postingState.event != event) {
        throw new EventBusException("Only the currently handled event may be aborted");
    } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
        throw new EventBusException(" event handlers may only abort the incoming event");
    }

    postingState.canceled = true;
}

相关文章

  • 对于EventBus的再次理解

    一、对于EventBus的再次理解:①EventBus.getDefault().postSticky(new U...

  • EventBus的理解

    一、Event的注册 使用方式: 所做的事情: 保存当前订阅类所订阅的所有事件类型,保存每种事件类型对应的Subs...

  • EventBus之3.0的使用

    一:EventBus简介 讲解怎么EventBus怎么用之前,先说说这玩意是干什么的。EventBus在我的理解中...

  • EventBus 源码分析

    EventBus原理解析 1. 注册EventBus 将一个类注册为事件的订阅者分两步 EventBus.getD...

  • EventBus源码学习笔记

    前言 最近阅读了EventBus(3.0.0)的源码,这里也是记录下自己对EventBus的理解,功力善浅,如有错...

  • 对eventBus的理解

    eventbus最早主要是通过注解方式实现的,最新的3.0版本主要是通过注解处理器来实现的,在代码的编译阶段产生模...

  • EventBus源码理解

    EventBus源码理解 EventBus是我们在开发中经常使用的开源库,使用起来比较简单,而且源码看起来不是很吃...

  • EventBus初理解

    缘由: 平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注...

  • EventBus初理解

    缘由平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注重积...

  • 十、EventBus 源码随想

    EventBus 源码随想 首先网上已经有不少优秀的EventBus的源码分析文章,这篇只是为了记录自己的理解,毕...

网友评论

      本文标题:EventBus的理解

      本文链接:https://www.haomeiwen.com/subject/ruadkftx.html