EventBus3.0源码分析

2019-03-05  本文已影响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清除

上一篇下一篇

猜你喜欢

热点阅读