美文网首页
EventBus3.0源码分析

EventBus3.0源码分析

作者: 那个那个谁哇 | 来源:发表于2019-03-05 20:07 被阅读0次

    一、EventBus的使用

    二、源码分析

    EventBus的使用一般分三步,1、注册、订阅,2、事件发送,3、解绑

    注册分析

    EventBus.getDefault().register(this),EventBus采用了单例模式

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

    register(this)注册到底做了什么?

    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(subscriberClass)这个方法从字面上我们就可以知道它的作用是拿到我们订阅的method,即使用注解@subscribe标注的方法。SubscriberMethod类包含一些方法的信息

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

    这个类包含线程模式、事件类型、优先级、是否是粘性事件,EventBus是如何获取被注解的方法信息的呢?有两种方法,1、通过反射获取,2、编译时注解生成索引,通过ignoreGeneratedIndex变量来判断是否使用索引

    1、通过放射

        private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
            FindState findState = prepareFindState();
            findState.initForSubscriber(subscriberClass);
            while (findState.clazz != null) {
                findUsingReflectionInSingleClass(findState);
                findState.moveToSuperclass();
            }
            return getMethodsAndRelease(findState);
        }
    

    findUsingReflection()会将subscriberClass类本身以及它的父类中的方法都遍历一遍,找到使用@subscribe注解的方法封装成一个SubscriberMethod对象。findUsingReflectionInSingleClass(findState)就是寻找findState.clazz 中注解的方法,结束后就将findState.clazz 的父类赋值给自身接着跑循环,所以最终父类注解的方法也被挑出来了。我们都知道反射会影响程序的性能,那么EventBus是如何解决的了?这就有了第二种方法

    2、编译时注解添加索引,解决反射的性能问题

    https://www.jianshu.com/p/96b198244cb5

    回到注册register(),获取到所有SubscriberMethod信息后,遍历它逐一调用subscribe(subscriber, subscriberMethod),subscribe()做了三件事,
    1、使用订阅者和订阅者的方法信息subscriberMethod生成Subscription对象,按优先级存入subscriptionsByEventType中
    2、记录订阅者的事件类型于typesBySubscriber
    3、判断subscriberMethod是否是粘性事件,是的话就将stickyEvents中存放的事件粘性事件投放给subscriberMethod

    事件发送

    发送事件分两种,1、post(),2、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);
        }
    

    postSticky()其实也是调用了post(),只是多了一步将事件存入stickyEvents中
    。stickyEvents如果不手动remove的话,会一直存在,前面注册的时候我们知道会触发粘性事件,所以后注册的订阅者只要有stickyEvents中对应的事件订阅还是会收到的。

        public void post(Object event) {
            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;
                }
            }
        }
    

    先将event存入eventQueue,然后遍历它postSingleEvent(eventQueue.remove(0), postingState),最终会调用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);
            }
        }
    

    调用invokeSubscriber(subscription, event)就会触发订阅方法,通过反射完成。我们这里的重点分析下它是如何切换线程的,这里有三个poster,mainThreadPoster、backgroundPoster、asyncPoster分别处理主线程、后台线程(非主线程则直接调用,主线程则开单线程调用)、异步线程(异步调用,使用线程池在不同的线程调用)。
    mainThreadPoster这个没啥好说的了,主线程Handler,其余两个都实现了Runnable且持有eventBus实例,主要区别就在于他们的run()实现,backgroundPoster只要一个线程中处理queue中的所有事件,queue结束为止,而asyncPoster会每次enqueue()都调用eventBus.getExecutorService().execute(this)也就是说所有事件可能在不同的线程执行(线程池复用线程),它们最终只是改变了eventBus.invokeSubscriber(pendingPost)的调用线程。

    解绑

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

    解绑只是将订阅者监听的事件类型以及相应的subscriptionsByEventType清除

    相关文章

      网友评论

          本文标题:EventBus3.0源码分析

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