美文网首页Android知识Android技术知识Android开发
EventBus源码解析(二)register与unregist

EventBus源码解析(二)register与unregist

作者: Hohohong | 来源:发表于2017-03-15 00:11 被阅读934次

    前边文章主要跟大家大概讲了下EventBus的用法和注解,接下来则是从源码角度来看EventBus的内部处理

    EventBus源码解析系列

    EventBus源码解析(一)关于用法和注解
    EventBus源码解析(二)register与unregister
    EventBus源码解析(三)Post方法和注解处理器

    首先我们从register方法看起

    EventBus.getDefault().register(this);
    

    getDefault则是用的一个双重校验锁来保证线程安全,保持一个单例

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

    它的构造方法则为

     public EventBus() {
            this(DEFAULT_BUILDER);
        }
    

    在这里DEFAULT_BUILDER是一个Builder,之后就根据Builder设定的值赋值给它的成员变量,我们主要看这几个变量

        private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        private final Map<Object, List<Class<?>>> typesBySubscriber;
        private final Map<Class<?>, Object> stickyEvents;
        
        private final HandlerPoster mainThreadPoster;
        private final BackgroundPoster backgroundPoster;
        private final AsyncPoster asyncPoster;
    
    • subscriptionsByEventType:以当前event事件类为key,订阅对象Subscription为value。事件发送之后,则是在这里寻找订阅者。在这里,Subscription则是一个封装了订阅者和方法体的一个类。

    • typesBySubscriber:以订阅类为Key,以event事件类为value,在进行register和unregister操作时会操作这个map

    • stickyEvents:保存的是粘性事件,关于粘性事件的设置以及使用上一篇文章则有说到。

    • 后面三个Poster则是用来处理粘性事件的,这个后面慢慢介绍到。

    看下register方法

    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.findSubscriberMethods获取到当前对象的所有方法,而方法则是封装在SubscriberMethod这个类中,获取到方法之后则遍历调用subscribe方法。

    我们来看一下EventBus内部是如何拿到一个类的方法的。

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
            //①默认false
            if (ignoreGeneratedIndex) {
                subscriberMethods = findUsingReflection(subscriberClass);
            } else {
                //②
                subscriberMethods = findUsingInfo(subscriberClass);
            }
            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;
            }
        }
    

    首先,它会根据当前类的key去缓存中寻找,如果缓存中有当前类的方法集合,则直接获取。当然,一开始则是为Null。

    接下来对ignoreGeneratedIndex进行判断,这里暂不解释它,默认值则为false,除非你在Builder自己设置,否则通过EventBus.getDefault()则为false。false后调用了findUsingInfo方法,这个方法则肯定是用来获取当前类的方法的。之后获取到方法之后则判断当前方法是否是public以及是否有设置注解@Subscribe,否则则抛出异常。

     private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
           //①获取FindState对象
            FindState findState = prepareFindState();
            //初始化
            findState.initForSubscriber(subscriberClass);
            while (findState.clazz != null) {
                //②获取订阅者的信息,一开始为null,如果有使用注解处理器,则不为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 {
                    //③通过反射来获取方法信息,之后保存在findState
                    findUsingReflectionInSingleClass(findState);
                }
                findState.moveToSuperclass();
            }
            //④从findState中获取到SubscriberMethod
            return getMethodsAndRelease(findState);
        }
    

    FindState主要用来保存订阅者的信息,内部同样有很多用来保存订阅信息的map

     //订阅方法的保存
     final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
     //以event为key,以method为value
     final Map<Class, Object> anyMethodByEventType = new HashMap<>();
     //以method的名字生成一个method为key,以订阅者类为value
     final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    

    首先看下上面流程

    • ①调用prepareFindState来获取一个FindState
     private FindState prepareFindState() {
            synchronized (FIND_STATE_POOL) {
                for (int i = 0; i < POOL_SIZE; i++) {
                    FindState state = FIND_STATE_POOL[i];
                    if (state != null) {
                        FIND_STATE_POOL[i] = null;
                        return state;
                    }
                }
            }
            return new FindState();
        }
    

    prepareFindState方法里面则会先去线程池中去查找有没有FindState没有则自己创建一个。然后调用initForSubscriber来初始化信息

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

    保存当前的订阅者

    • ②通过getSubscriberInfo(findState);根据当前的findState来获取订阅者的信息。
     private SubscriberInfo getSubscriberInfo(FindState findState) {
            //新创建的subscriberInfo为null
            if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
                SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
                if (findState.clazz == superclassInfo.getSubscriberClass()) {
                    return superclassInfo;
                }
            }
            //判断有没有使用注解处理器,如果有使用,则在编译器时候通过读取@Subscribe()注解并解析保存到subscriberInfoIndexes中了。
            if (subscriberInfoIndexes != null) {
                for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                    SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                    if (info != null) {
                        return info;
                    }
                }
            }
            return null;
        }
    

    可以说这个方法主要判断有没有使用注解处理器,默认的情况下是没有使用的,关于注解处理器这个后面会讲。

    • ③调用findUsingReflectionInSingleClass(findState);方法,根据当前findState来反射获取方法。
    private void findUsingReflectionInSingleClass(FindState findState) {
            Method[] methods;
            try {
                //获取当前类所有的方法
                methods = findState.clazz.getDeclaredMethods();
            } catch (Throwable th) {
                methods = findState.clazz.getMethods();
                findState.skipSuperClasses = true;
            }
            for (Method method : methods) {
                int modifiers = method.getModifiers();
                //判断当前方法的类型是否是public,以及有没有使用注解@Subscribe
                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];
                            //调用checkAdd进行检查
                            if (findState.checkAdd(method, eventType)) {
                               //获取模式
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                               //创建一个SubscribeMethod并保存到findState里面。
                               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");
                }
            }
        }
    

    可以看到整个反射的流程很清晰,先反射获取方法,然后进行方法判断,不满足情况则抛出异常,接着则获取方法信息保存在findState中
    通过反射将获取到的方法信息保存在FindState后,之后调用getMethodsAndRelease(findState);方法来从FindState中获取方法集合。

    之后回到register方法,调用subscrib

      public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    前面都是调用findSubscriberMethods来获取一个类中的注解方法,接下来则是调用了subscribe方法;

     private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            //①根据方法和订阅者构建一个Subscrption,将记录保存在subcriptionByEventType
            Class<?> eventType = subscriberMethod.eventType;
            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
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, 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);
                }
            }
        }
    

    subscribe方法我们分为4部分

    • ①第一部分比较好理解,根据方法和订阅者构造一个Subsciption用来保存订阅者信息,然后将信息保存在subscriptionsByEventType中,而这里的key则是eventType,关于这个eventType说成是事件类型可能大多数很懵比,但如果说成是方法的参数类型的话就好理解了。EventBus的接收事件则是根据这个eventType来判断是否是同个接收事件的,如果两个方法的参数名一样,那么都会接收到事件。

    • ②第二部分则是根据在@Subscribe注解中设定的优先级去整理subscriptions里面的顺序问题

    • ③第三部分则是把事件类型(参数类型)保存到typesBySubscriber。之后就可以根据Post的参数从typesBySubscriber中拿到对应的eventType,再到subscriptionsByEventType根据key eventType拿到订阅方法区调用。

    • if (subscriberMethod.sticky)这里面则是关于粘性事件的处理,关于粘性事件的设置处理情况我们在第一篇文章中也说过了,会设置粘性的情况在于发送事件的时机早于注册时机,此时如果使用了postSticky发送并且@Subscribe注解指定sticky = true的话则可以在注册后触发最近的一个事件,而该事件的触发就是在这里。接下来我们重点看这个if里面

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

    在这里eventInheritance默认是true,表示事件要考虑子类问题。找到对应的类后最后调用的是这来两个方法

      Object stickyEvent = stickyEvents.get(eventType);
      checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    

    stickyEvents在开头介绍3个成员变量时候说到,它是用来保存粘性事件的,那么它的添加Put是在哪里呢,没错就是postSticky

      public void postSticky(Object event) {
            synchronized (stickyEvents) {
                stickyEvents.put(event.getClass(), event);
            }
            // Should be posted after it is putted, in case the subscriber wants to remove immediately
            post(event);
        }
    

    这里stickyEvents的key则是为参数类型,所以对于粘性事件中同个参数类型的话只会保留最后使用的那个。这点在第一篇文件也有提及到。
    获取到事件后看它是怎么处理的。

     private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
            if (stickyEvent != null) {
                postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
            }
        }
    

    先做判空,之后发送给订阅者,postToSubscription就是最关键的方法了。

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
            switch (subscription.subscriberMethod.threadMode) {
                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);
            }
        }
    

    在这里,它会根据当前方法的注解的ThreadMode来选择对应的发送模式,关于ThreadMode之间的用法区别可以参照第一篇文章。
    这里我们就挑选MainThread来讲吧。
    MAIN:这里会先判断当前的执行线程是否是主线程,如果是则直接执行invokeSubscriber直接反射调用方法,否则则调用mainThreadPoster.enqueue等待事件发送到主线程调用。

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

    invokeSubscriber比较简单,最终是调用的方法的反射调用。
    对于mainThreadPoster.enqueue(subscription, event);mainThreadPoster是一个HandlerPoster对象,这个HandlerPoster对象则是继承与Handler,内部维护了一个事件订阅调用队列,把每一个事件调用都放到这个队列中,等待调度执行。

        private final HandlerPoster mainThreadPoster;
    

    HandlerPoster里面也比较好容易理解

    final class HandlerPoster extends Handler {
        private final PendingPostQueue queue;
        private final EventBus eventBus;
        ...
        HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage{
            super(looper);
            ...
        }
        void enqueue(Subscription subscription, Object event) {
            PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
            synchronized (this) {
                queue.enqueue(pendingPost);
                if (!handlerActive) {
                    handlerActive = true;
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                }
            }
        }
    
        @Override
        public void handleMessage(Message msg) {
            boolean rescheduled = false;
            try {
                long started = SystemClock.uptimeMillis();
                while (true) {
                    PendingPost pendingPost = queue.poll();
                   ...
                    }
                    eventBus.invokeSubscriber(pendingPost);
                   ...
                }
            } finally {
                handlerActive = rescheduled;
            }
        }
    }
    

    它最终也只是在handlerMessage调用了invokeSubscriber方法,但是它是怎么运行在主线程的呢?这个时候如果你懂得Handler消息传递机制的话就应该知道关键在于Looper,我们看EventBus中它的创建

      EventBus(EventBusBuilder builder) {
            subscriptionsByEventType = new HashMap<>();
            typesBySubscriber = new HashMap<>();
            stickyEvents = new ConcurrentHashMap<>();
            mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
            ...
      }
    

    在这里传递的是MainLooper,而Looper里面则有一个Thread对象,这里对应则是主线程。
    到这里register部分也讲完,就讲下unregister方法吧,而post方法和注解处理器则放到下一篇。

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

    记得我们之前提到的三个Map

        private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        private final Map<Object, List<Class<?>>> typesBySubscriber;
        private final Map<Class<?>, Object> stickyEvents;
    

    前面有说到typesBySubscribersubscriptionsByEventType的关系,typesBySubscriber是根据当前订阅者作为Key,value则为事件类型eventType,而subscriptionsByEventType则是根据事件类型eventType作为key,value为方法。关系调用则为

    Subscriber -> typesBySubscriber -> eventType -> subscriptionsByEventType -> method

    这unregister这里根据当前订阅者找到所有的事件类型(参数类型),
    然后调用unsubscribeByEventType方法

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

    可以看到,根据前面的eventType,作为key然后一步一步的移除注册。

    unregister方法也比较好理解。

    流程图小结

    小结register流程

    register流程

    相关文章

      网友评论

        本文标题:EventBus源码解析(二)register与unregist

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