EventBus源码详解,看这一篇就够了

之前写过一篇关于EventBus的文章,大家的反馈还不错(EventBus3.0使用详解),如果你还没有使用过EventBus,可以去那篇文章看看。当时刚接触EventBus,对它的理解也仅仅是停留在表面,写那篇文章也是记录下EventBus的一个简单的使用的过程,而如今随着EventBus在项目中的多次运用,个人感觉,会用一个框架跟知道它的原理这是两码事,所以下定决心写这篇文章来帮助自己更好的理解EventBus。

在进入主题之前,我们先保持着这样几个疑问,EventBus的使用三要素里,我们为什么要去定义事件方法,并且用到了@subscribe()注解? EventBus.getDefault().register(Object)这行代码到底干了什么?发送事件的时候又做了哪些操作?为什么要在onDestory()做解除绑定的操作...等等

(一) 注册: EventBus.getDefault().register(obj)

首先调用EventBus的静态getDefault()方法返回一个EventBus对象,采用单例实现。

//  静态的单例
static volatile EventBus defaultInstance;

// 默认的EventBusBuilder对象
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

//  使用了单例的双重锁机制
public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

 //  调用带参数的构造方法
 public EventBus() {
      this(DEFAULT_BUILDER);
 }

//  
 EventBus(EventBusBuilder builder) {
        //...       
       subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
 }

在EventBus(builder)构造方法里初始化了一些配置信息。

之后调用了EventBus的register(obj)方法,这个方法接收的参数类型是Object。这里我们分步骤1和步骤2去看看做了哪些操作。

public void register(Object subscriber) {
        //  通过反射拿到传入的obj的Class对象,如果是在MainActivity里做的注册操作,
        //  那subscriber就是MainActivity对象
        Class<?> subscriberClass = subscriber.getClass();
        // 步骤1 
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        //  步骤2
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

步骤1

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

首先subscriberMethodFinder对象是在EventBus带参数的构造函数里进行初始化的,从这个findSubscriberMethods()方法名就可以看出来,步骤1的是去获取当前注册的对象里所有的被@Subscribe注解的方法集合,那这个List集合的对象SubscriberMethod又是什么东东呢? 我们去看一下

public class SubscriberMethod {
    final 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;
    }
}
...

看完这个类里定义的信息,大概明白了。SubscriberMethod 定义了Method方法名,ThreadMode 线程模型,eventType 事件的class对象,priority是指接收事件的优先级,sticky是指是否是粘性事件,SubscriberMethod 对这些信息做了一个封装。这些信息在我们处理事件的时候都会用到:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void XXX(MessageEvent messageEvent) {
    ...
}

好的,知道了SubscriberMethod 是什么东东后,我们直接进入findSubscriberMethods(subscriberClass)方法。

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
         //  METHOD_CACHE是事先定义了一个缓存Map,以当前的注册对象的Class对象为key,注册的对象里所有的被@Subscribe注解的方法集合为value
         List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);

        // 第一次进来的时候,缓存里面没有集合,subscriberMethods 为null
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // ignoreGeneratedIndex是在SubscriberMethodFinder()的构造函数初始化的,默认值是 false
        if (ignoreGeneratedIndex) {
            //  通过反射去获取 
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
           //  通过apt插件生成的代码。使用subscriber Index生成的SubscriberInfo来获取订阅者的事件处理函数,
            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;
        }
    }

首先是从缓存中获取,如果缓存中存在直接返回,这里使用缓存有一个 好处,比如在MainActivity进行了注册操作,多次启动MainActivity,就会直接去缓存中拿数据。如果缓存里没有数据,就会根据ignoreGeneratedIndex 这个boolean值去调用不同的方法,ignoreGeneratedIndex 默认为false。如果此时,获取到的方法集合还是空的,程序就会抛出异常,提醒用户被注册的对象以及他的父类没有被@Subscribe注解的public方法(这里插一句,很多时候,如果打正式包的时候EventBus没有做混淆处理,就会抛出该异常,因为方法名被混淆处理了,EventBus会找不到),把获取到的方法集合存入到缓存中去,并且把方法集合return出去。

大致的流程就是这样,findSubscriberMethods()负责获取注册对象的方法集合,优先从缓存中获取,缓存中不存在,则根据ignoreGeneratedIndex是true或false,如果ignoreGeneratedIndex为true,则调用findUsingReflection(),如果为false,则调用findUsingInfo()方法去获取方法集合

findUsingInfo():

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        // 这里采用了享元设计模式
        FindState findState = prepareFindState();
        // 初始化FindState里的参数
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != 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 {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

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

// FindState类定义的信息
static class FindState {
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        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;
        }
....
....
}

获取FindState采用了享元模式,这里不多说。之后调用了 findState.initForSubscriber(subscriberClass)方法,只是为了给FindState对象里的一些信息进行赋值操作。再然后是一个 while循环,循环里首先调用getSubscriberInfo(findState)方法,点进去看一下,发现该方法返回null。这里留一个小疑问,getSubscriberInfo()方法什么时候不为null?? 答案在最后一节的高级用法里

private SubscriberInfo getSubscriberInfo(FindState findState) {
        //  findState.subscriberInfo在初始化的时候置为null,所以该if 分支不会执行

        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
       //  subscriberInfoIndexes初始化的时候也是null,并没有赋值
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

所以findUsingInfo()的while循环里直接走了else分支:findUsingReflectionInSingleClass(findState)。

也就是说findUsingInfo()方法的主要逻辑是由findUsingReflectionInSingleClass()方法去完成的(默认情况,不考虑使用apt)

这里有个细节要看一下:

while (findState.clazz != 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 {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }

这个while循环什么时候结束呢?这是我们第一次看EventBus源码看到这里比较疑惑的地方,答案就在这个 findState.moveToSuperclass()里面。

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

我们可以看到去把clazz赋值为它的超类,直到没有父类为止,才返回clazz=null,循环也才终止。也就是说 这个while循环保证了,获取注解的方法不仅会从当前注册对象里去找,也会去从他的父类查找

好了,继续分析findUsingReflectionInSingleClass(findState)方法。

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
           // 查找注册对象的所有方法,注意是所有
            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) {
            //  获取该方法的修饰符,即public、private等
            int modifiers = method.getModifiers();

            // 修饰符是public才会走该分支
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 这里是回去该方法的参数类型,String,in
                Class<?>[] parameterTypes = method.getParameterTypes();
  
               //  只有一个参数会走该分支
                if (parameterTypes.length == 1) {

                    //  如果该方法被@subscribe注解会走该分支
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                    //  获取传入的对象的Class
                        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");
            }
        }
    }

findUsingReflectionInSingleClass()方法首先通过反射去拿到当前注册对象的所有的方法,然后去进行遍历,并进行第一次过滤,只针对修饰符是Public的方法,之后进行了第二次过滤,判断了方法的参数的个数是不是只有一个,如果满足,才去进一步的获取被@subscribe注解的方法。

然后调用
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()))这行代码,new了一个SubscriberMethod()对象,传入参数,并添加到 findState.subscriberMethods的集合中去.

 static class FindState {
       final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
 }

之后,findUsingInfo()的getMethodsAndRelease(findState)方法回去获取刚刚设置的findState的subscriberMethods集合,并把它return出去。代码如下:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        //  对subscriberMethods进行了赋值,return出去
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
       //  进行了回收
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

步骤1总结:至此,以上就是EventBus获取一个注册对象的所有的被@subscribe注解的方法的集合的一个过程。该过程的主要方法流程为:

(1) subscriberMethodFinder.findSubscriberMethods()
(2) findUsingInfo()
(3) findUsingReflectionInSingleClass()

步骤2

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

通过步骤1,我们已经拿到了注册对象的所有的被@subscribe注解的方法的集合的。现在我们看看subscribe()都做了哪些 操作。

我们不妨想想,如果我们要去做subscribe()时,我们要考虑哪些问题,第一个问题是,要判断一下这些方法是不是已经注册过该事件了要不要考虑方法名是不是相同的问题。第二个问题是一个注册对象中有多个方法注册了该事件,我们该怎么保存这些方法,比如说事件类型是String,一个Activity里有两个方法注册了该事件,分别是onEvent1和onEvent2,那我是不是应该用一个Map集合,以事件类型为key,把onEvent1和onEvent2放到一个List集合中,把该List集合作为value。

subscribe()方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 拿到事件event类型,比如是String或者自定义的对象
        Class<?> eventType = subscriberMethod.eventType;

        //  Subscription将注册对象和subscriberMethod 做为参数传入

        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
  
       // subscriptionsByEventType是一个Map集合,key是事件类型,验证了我上面的猜想
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

       // 如果subscriptions是null,则new出一个CopyOnWriteArrayList,并且往Map集合中添加
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
      
      //  这里做了if语句判断,判断一下List集合中是否存在,存在就抛异常
      //  如果不存在?怎么没有add操作? 保持疑问

            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

以上的操作验证了我之前的猜想,通过if (subscriptions.contains(newSubscription)) 这个if语句判断 是否发生了重复注册,注意这里重复注册的含义是 事件类型一致,以及方法名也一致。

接下来我们看看如果一个注册对象重复注册了事件Event(方法名不能一致),优先级priority是如何设置的

  int size = subscriptions.size();
   for (int i = 0; i <= size; i++) {

      //  这里判断subscriberMethod的优先级是否是大于集合中的subscriberMethod的优先级,如果是,把newSubscription插进去
     //  这也表明了subscription中priority大的在前,这样在事件分发时就会先获取。
             if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

if语句的条件subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) ,保证了subscription中priority大的在前。同时i == size 这个条件也保证了priority小的也会添加到subscriptions集合中去

紧接着我们看看EventBus是如何处理粘性事件的

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

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

注意以上代码有四行比较重要的注释信息。大致的意思是必须考虑eventType所有子类的现有粘性事件,在迭代的过程中,所有的event可能会因为大量的sticky events变得低效,为了使得查询变得高效应该改变数据结构。
isAssignableFrom方法的意思是判断candidateEventType是不是eventType的子类或者子接口,如果postSticky()的参数是子Event,那么@Subscribe注解方法中的参数是父Event也可以接收到此消息。

拿到粘性Event后,调用了checkPostStickyEventToSubscription()方法,改方法内部方法内部调用了postToSubscription()

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        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());
        }
    }

步骤2总结:至此,EventBus的注册操作已经全部分析完了,需要注意的是,粘性事件是在subscribe中进行post的

(二) 发送事件:EventBus.getDefault().post(xxx);

普通Event

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
       //  将Event添加到List集合中去
        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 {
               // 遍历这个list集合,条件是集合是否是空的
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

首先将当前的 Event添加到eventQueue中去,并且while循环,处理post每一个Event事件,调用的是 postSingleEvent(eventQueue.remove(0), postingState);

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //  获取Event的Class对象
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;

       //  eventInheritance初始化的时候值为true,所以会走该分支
        if (eventInheritance) {
       //  获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
    ...
    ...
}

这里lookupAllEventTypes()方法也是为了获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。
之后是 for循环获取到的Class对象集合,调用postSingleEventForEventType()方法。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        // subscriptionsByEventType该map是在subscribe()方法中进行了put操作
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                   // 进行for循环并调用了postToSubscription()
                    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;
    }

postSingleEventForEventType()方法,主要是获取Event的Class对象所对应的一个List集合,集合的对象是Subscription参数。subscriptionsByEventType对象是在subscribe()方法中进行了赋值。

for循环CopyOnWriteArrayList集合,并调用postToSubscription();

线程模型

等执行到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);
        }
    

第一个分支:线程模型是POST,直接调用了invokeSubscriber()方法。

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

很明显的看到,这是基于反射去调用方法,invoke方法接收两个参数,第一个参数是注册的对象,第二个参数是事件的Event。

从这里就可以看出来,POST并没有去做线程的调度什么的,事件处理函数的线程跟发布事件的线程在同一个线程。

第二个分支:线程模型是MAIN 首先判断了下事件发布的线程是不是主线程,如果是,执行invokeSubscriber()方法,invokeSubscriber()上面已经分析过,如果不是主线程,执行mainThreadPoster.enqueue(subscription, event)。
mainThreadPoster是继承自Handler,从这里大概可以猜到,这一步是去做线程调度的。

我们看一看mainThreadPoster的enqueue做了什么事:

void enqueue(Subscription subscription, Object event) {
        //  封装了一个PendIngPost
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // 将PendIngPost压入队列
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                //  调用了sendMessage()
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

enqueue() 主要封装了一个PendingPost类,并把subscription和event作为参数传进去,紧接着把PendingPost压入到队列中去,然后发了一条消息sendMessage。

熟悉Handler机制的同学知道,处理消息是在handleMessage()方法中完成的:

public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

代码有点多,我们主要看一下,它接收到消息后,是做了什么处理。从队列中取了消息,并且调用了eventBus.invokeSubscriber(pendingPost)方法,回到EventBus类中。

void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {
            invokeSubscriber(subscription, event);
        }
    }

该方法内部还是去调用了invokeSubscriber()方法。

分析完线程模型为MAIN 的工作流程,不难做出结论,当发布事件所在的线程是在主线程时,我们不需要做线程调度,直接调用反射方法去执行。如果发布事件所在的线程不是在主线程,需要使用Handler做线程的调度,并最终调用反射方法去执行

第三个分支:线程模型是BACKGROUND。如果事件发布的线程是在主线程,执行
backgroundPoster.enqueue(subscription, event),否则执行invokeSubscriber()。

backgroundPoster实现了Runable接口:

 public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

将PendingPost对象压入队列,然后调用eventBus.getExecutorService().execute(this),交给线程池去进行处理,它的处理是在Runnable的run()中。

@Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

最重要的还是eventBus.invokeSubscriber(pendingPost)这行代码,上面已经分析过。

第四个分支:线程模型是ASYNC。直接调用 asyncPoster.enqueue(subscription, event),asyncPoster也是实现了Runnable接口,里面也是使用的线程池,具体的操作就不分析了,感兴趣的可以去看一下源码,跟上一步操作类似。

(三) 高级用法

EventBus3.0较之前的版本有了一次改造,在3.0之后增加了注解处理器,在程序的编译时候,就可以根据注解生成相对应的代码,相对于之前的直接通过运行时反射,大大提高了程序的运行效率,但是在3.0默认的还是通过反射去查找用@Subscribe标注的方法,一般在使用的时候基本都是这个模式。 那我们怎么配置让EventBus使用注解器生成的代码呢?

EventBus官网apt介绍

在这里我们重点提一下 EventBusBuilder类的:

boolean ignoreGeneratedIndex;
List<SubscriberInfoIndex> subscriberInfoIndexes;

subscriberInfoIndexes变量可以去使用注解处理器生成的代码。SubscriberInfoIndex 就是一个接口,而注解生成器生成的类也是继承的它,我们也可以自己去继承它,定制自己的需求,不需要反射的EventBus。

我们再回过头来看一下注册过程的findUsingInfo()方法:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != 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 {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

我们在前面分析的时候,直接分析的 findUsingReflectionInSingleClass(findState)方法,因为getSubscriberInfo()返回null,那什么时候getSubscriberInfo()返回不为null呢 ? 我们具体看看getSubscriberInfo()。

private SubscriberInfo getSubscriberInfo(FindState findState) {
       if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
           SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
           if (findState.clazz == superclassInfo.getSubscriberClass()) {
               return superclassInfo;
           }
       }
      //  判断subscriberInfoIndexes 是否为null,默认为null,当我们使用apt插件构建代码 的时候,可以手动的调用EventBusBuilder的addIndex,将subscriberInfoIndexes 进行赋值。
       if (subscriberInfoIndexes != null) {
           for (SubscriberInfoIndex index : subscriberInfoIndexes) {
               SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
               if (info != null) {
                   return info;
               }
           }
       }
       return null;
   }

当我们使用apt插件构建代码 的时候,可以手动的调用EventBusBuilder的addIndex(),将subscriberInfoIndexes 进行赋值。这样subscriberInfoIndexes 就不会为null,getSubscriberInfo()方法也就不会为null。findUsingInfo()也就不会调用反射去获取数据,从而提高了性能。

如何使用新特性SubscriberIndex:

在gradle文件做以下配置:

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
            }
        }
    }
}

dependencies {
    compile 'org.greenrobot:eventbus:3.1.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

成功构建项目后,将为您生成使用eventBusIndex指定的类 。然后在设置EventBus时将其传递给它:

 EventBus.builder().ignoreGeneratedIndex(false).addIndex(new MyEventBusIndex()).installDefaultEventBus();
                           

我们回过头看,注解器还是很有用的,避免使用反射去消耗了性能。

总结:无论是EventBus还是ButterKnife,都使用到了注解处理器,这方面的知识还是要多去学习。还有的是对一个框架不仅仅要知道怎么调用它的api,还要尽可能的从源码的角度去分析它的实现原理,阅读源码也是一个自我提升的一个过程!

Android的技术讨论Q群:947460837

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容