EventBus源码分析

EventBus源码分析

Android开发中我们最常用到的可以说就是EventBus了,今天我们来深入研究一下EventBus的源码。

使用简介

先简单回顾下EventBus最基本的使用方法:

  • 首先创建一个数据类
public class MessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}
  • 在相关的代码中添加处理事件的方法:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
  • 在相应的生命周期中注册/解绑
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}
  • 最后一步,在你需要的地方发送相应的事件
EventBus.getDefault().post(new MessageEvent("Hello world!"));

源码分析

使用EventBus基本需要四步,我们分析源码主要着重与后两步,也就是三个方法: EventBus.getDefault().register(this);EventBus.getDefault().post(new MessageEvent("Hello world!"));EventBus.getDefault().unregister(this);。以这三个方法为入口来切入EventBus的源码分析,会使我们对EventBus的整个流程更加清晰。

register方法

public void register(Object subscriber) {
    
         //1.获取订阅者的Class对象
        Class<?> subscriberClass = subscriber.getClass();
        
        //通过反射获取订阅者中开发人员定义的处理事件的方法集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        
        //将获取的订阅者和订阅者中处理事件的方法按照一定的规则相结合并存储到EventBus内部
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

我把register这个方法主要分为三部分。第一步很简单了,获取订阅者的Class对象,这个订阅者通常就是我们写的Activity或者Fragment等等。第二步是通过我们第一步拿到的订阅者的Class对象,反射获取所有符合规则的处理事件的方法。既然要 符合规则的方法 ,那么规则是什么呢?其实规则就是这些方法必须加 @subscribe 注解,参数必须有且只有一个等等。我们来看一下代码:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    
        //通过缓存获取处理事件方法集合,如果有缓存从缓存中取,如果没有则进行下一步
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        //获取处理事件方法集合
        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;
        }
    }

findSubscriberMethods这个方法中,主要是进行了对订阅者中我们定义的处理事件的方法的获取操作。代码逻辑其实已经比较清晰了,首先是从缓存中拿方法集合,有的话直接返回,没有的话再去获取。第二步是获取的关键,它比较复杂,我们暂时先看第三步。如果当前获取到的方法集合为空,则抛出异常。看到这里小伙伴们应该就比较熟悉了,在我们刚开始使用EventBus的时候经常会在一个Activity中调用的EventBus的register方法,但是却忘了写@subscribe注解的方法,运行程序会报错。那么很明显,就是这里抛出了一个异常,这个异常提醒我们,如果在订阅者中调用的register方法,就必须添加至少一个相应的处理事件的方法。如果获取到的方法集合不为空,则将之放入缓存并返回。

这个findSubscriberMethods方法中最关键的是第二步的获取。这里有一个变量 ignoreGeneratedIndex,是否忽略索引,默认值为false,也就是默认不忽略索引,走的是else中的 findUsingInfo(subscriberClass); 方法。那么问题来了,这个索引是什么,为什么我们平常使用EventBus的时候从来都没用过这个东西?其实在EventBus3.0之前是没有索引这个东西的,第二步获取方法集合走的是 findUsingReflection(subscriberClass); 这个方法。那为什么在3.0之后加上了索引呢?其实答案很简单,就是因为反射获取方法集合是比较低效的,使用反射所耗费的时间开销比较大。如果使用索引,EventBus的性能会提升一倍。具体如何在你的代码中使用EventBus的索引,可以参考 greenrobot官网中index的介绍 。那么小伙伴们问题又来了,既然是默认不忽略索引,而我们平时使用EventBus根本没添加过索引相关的代码,那 findUsingInfo(subscriberClass); 这段代码究竟是使用什么方式来获取方法集合的呢?放代码:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            
            //如果我们没有添加相关索引的配置,那么findState.subscriberInfo=null,走的是else中的方法
            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);
    }

findUsingInfo 这段代码中,我们忽略细节,只看我添加注释的部分。如果添加了索引相关的配置,代码会去执行索引相关的逻辑;反之,代码执行 findUsingReflectionInSingleClass(findState); 这个函数,通过反射来获取方法集合。我们忽略索引部分,跟进 findUsingReflectionInSingleClass(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();
            
            //必须是PUBLIC方法.....
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                
                //参数个数只能为1
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    
                    //必须有@subscribe注解
                    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");
            }
        }
    }

这个方法就很清楚了。首先通过反射拿到所有方法的集合,然后遍历这个集合,筛选出符合规则的方法,也就是我们写的处理事件的方法。到这里小伙伴们应该很清楚了,变量ignoreGeneratedIndex默认是false,走的是索引的处理,但是我平时没写过索引的配置,所以最后还是走的反射,通过反射获取方法集合。

register方法中的第二步到这里就大致说完了,下面我们开始第三步。代码上面有,我再贴一遍方便大家看。

synchronized (this) {
            //遍历方法集合
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }

这段代码很简单,遍历第二步获取的方法集合,调用 subscribe(subscriber, subscriberMethod); 这个方法。很明显,subscribe(subscriber, subscriberMethod); 是最后一步的核心代码,我们跟进去看一下:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        
        //获取方法中的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        
        //将订阅者和方法绑在一起形成一个Subscription对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        
        //将newSubscription加入eventType所对应的subscriptions集合中
        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();
        //按照priority优先级将newSubscription加入subscriptions集合中
        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);

        //如果方法可以接收sticky事件
        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>).
                
                //遍历stickEvents这个map
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    
                    //如果找到了跟这个方法的eventType一致的stickEvent
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        
                        //发送事件,本质是通过反射直接调用这个方法
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                //
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

这段代码就比较多了,我们来捋一下它的思路。这个方法接收2个参数Object subscriber, SubscriberMethod subscriberMethod,第一个参数是我们的订阅者,第二个参数是对我们写的处理事件的方法的封装。方法内部首先通过Class<?> eventType = subscriberMethod.eventType;获取了一个eventType,代表的是处理事件方法中参数的类型,比方说:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

在这个例子中eventType指的就是MessageEvent.Class。下一步通过Subscription newSubscription = new Subscription(subscriber, subscriberMethod);将订阅者和方法封装绑定。接下来的话subscriptionsByEventType这个变量是EventBus中的一个成员变量,通过名字我们也可以知道,这个一个map,它的主要功能就是通过eventType来获取subscriptions这个Subscription类的集合。我们看一下这个成员变量的定义:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

跟我们说的一样,subscriptionsByEventType是一个map,它的key存的是eventType,value存的是Subscription的集合。继续看下一段代码:

 //按照priority优先级将newSubscription加入subscriptions集合中
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;
            }
        }

记不记得如果要确保事件的处理需要有先后顺序,那么我们需要在事件处理方法上添加priority的注解。这段代码就是把优先级高的放到list的首部,优先级低的放到后面,通过这种方式实现了不同方法对同一事件处理的优先级问题。继续看代码:

//初始化 typesBySubscriber 这个成员变量,形成 订阅者 -> 订阅者能够接收的事件类型集合 的关系
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

这段代码核心是初始化typesBySubscriber这个成员变量,它的结构和subscriptionsByEventType类似,key是subscriber订阅者,value是eventType的集合。接下来是最后一段代码:

 //如果方法可以接收sticky事件
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>).
                
                //遍历stickEvents这个map
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    
                    //如果找到了跟这个方法的eventType一致或是其子类的stickEvent
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        
                        //发送事件,本质是通过反射直接调用这个方法
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }

粘性事件相信大家都用过,这段代码首先判断了处理事件的方法是否有stick这个注解,如果存在则进入下一步。eventInheritance这个变量默认为true,代表事件是否具有继承性。可以这么理解,发送一个事件或者发送这个事件类型的子类,EventBus的处理事件方法中的参数类型是父类型,那么这个方法既可以收到父类型的事件,也可以收到子类型的事件。那小伙伴们要问了,那如果处理事件方法中的参数类型是子类型呢?那这个方法就只能接收子类型的事件,而无法接收父类型的事件。在if (eventType.isAssignableFrom(candidateEventType))这一行代码做了判断,isAssignableFrom这个方法是JDK中Class类中的一个方法,作用就是判断前者是否是与后者类型一致或前者是后者的父类。下面这个代码就很简单了checkPostStickyEventToSubscription(newSubscription, stickyEvent);发送事件,我们跟进去看一下:

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

这个方法的功能很简单,对事件做了非空校验,然后调用了postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());方法,这个方法本质就是通过反射调用method.invoke(subscription.subscriber, event);来执行我们定义的对事件的处理方法。这个方法很重要,我会在讲post方法的时候着重分析,我们暂时只需要知道它的功能即可。看到这里,小伙伴们应该就能明白,为什么在粘性事件发出后再注册的事件处理方法可以接收到它了,因为之前发送的stickyEvent都会存入stickyEvents这个map类型的EventBus中的成员变量中,当我们注册调用register方法时,在register方法内部会直接通过checkPostStickyEventToSubscription(newSubscription, stickyEvent);来执行我们定义的粘性事件处理方法。

至此,我们终于说完了register这个方法,这个方法的内容看似只有短短几行,但它所做的事情还是很繁琐复杂的。下面我们来对上面对register的分析来做个总结:

  • 通过反射获取订阅者中定义的处理事件的方法集合
  • 遍历方法集合,调用subscribe方法,在这个subscribe方法内:
    • 初始化并完善subscriptionsByEventType这个map对象
    • 初始化并完善typesBySubscriber这个map对象
    • 触发stick事件的处理

post方法

我们常用的post有两种:post和postSticky,先来看postSticky:

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            
            //所有发出的sticky事件都会存入stickyEvents这个map中
            stickyEvents.put(event.getClass(), event);
        }
        // 调用普通的post方法
        post(event);
    }

postSticky结构分为两块:将所有发出的sticky事件存入stickyEvents这个map中,关于stickyEvents这个EventBus中的成员变量我们在register方法中已经说过,不再赘述。之后会调用post方法,所以我们的重点还是post方法:

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循环来不断发送事件队列中的事件,直到事件队列为空
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

忽略代码细节的话,post还是很好理解的。在发送事件的当前线程中会存储一个线程状态的变量postingState,这个变量包含了当前线程中的一些重要信息,我们来看一下它的组成:

final static class PostingThreadState {
        //事件队列
        final List<Object> eventQueue = new ArrayList<Object>();
        //是否正在发送事件
        boolean isPosting;
        //是否是主线程
        boolean isMainThread;
        Subscription subscription;
        Object event;
        //是否已经取消
        boolean canceled;
    }

可以看到,PostingThreadState为当前线程存储了很多有用的信息:事件队列、是否正在发送事件、是否是主线程、subscription、事件、是否已经取消。

继续看post的代码,将发送的事件加入到了当前线程中的事件队列中。之后就是一个while循环,不断发送事件队列中的事件,直到事件队列中不再包含任何事件。循环中发送事件的方法是postSingleEvent,我们跟进去看一下是如何发送的:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    
        //获取事件类型
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        
        //事件是否具有继承性,默认为true
        if (eventInheritance) {
            
            //查询事件所有的子类型
            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);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

这个方法核心只有两部分:获取事件类型、发送事件。有一个变量大家可能会有些疑惑,eventInheritance代表是否允许事件发送这个事件的所有父类型事件,默认为true,也就是说在发送一个子类事件时,EventType是父类的事件处理方法也可以收到当前事件。举个栗子:

@Subscribe()
    public fun onReceiveObject(data: Any) {
        Observable.timer(
                1,
                TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe() {
            toast("any")
        }

    }

    @Subscribe()
    public fun onReceiveDataItem(data: DataItem) {
        Observable.timer(
                2,
                TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe() {
            toast("dataItem")
        }
    }

这是两个kotlin写的事件处理方法,第一个接收的事件类型是Any,对kotlin不熟悉的小伙伴可以理解为Object类型;第二个接收的事件类型是DataItem。我们都知道,Object类肯定是DataItem的父类。我下面提两个问题,如果发送Object类型的事件,会发生什么?如果发送DataItem类型的事件,会发生什么?

相信如果大家真正理解了postSingleEvent这个方法的话,这个问题不难回答。如果发送Object类型,会显示any的Toast;如果发送DataItem类型,则会显示anydataItem的Toast。从上面的代码分析我们可以知道,在发送一个事件的时候,EventBus默认会找到这个事件类型所有的父类型并发送。

postSingleEvent这个方法中发送事件的方法是postSingleEventForEventType,我们继续跟进去看代码:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        
        //根据事件类型找到subscriptions
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        
        //变量subscriptions,发送事件
        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;
    }

代码本事逻辑很简单:根据subscriptionsByEventType找到事件eventClass对应的subscriptions,相信这些变量大家都很熟悉了。之后会遍历subscriptions,调用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);
        }
    }

整段代码按照线程分为四个部分来处理。POSTING代表着当前线程,在哪个线程发送事件,就在哪个线程处理事件;MAIN代表只在主线程处理事件;BACKGROUND代表只在非主线程处理事件;ASYNC也是代表在非主线程处理事件。

BACKGROUNDASYNC都是在非主线程处理事件,那么二者有什么区别呢?
从代码中可以直观的看到:BACKGROUND的逻辑是如果在主线程,那么会开启一条新的线程处理事件,如果不在主线程,那么直接处理事件;而ASYNC的逻辑则是不管你处于什么线程,我都新开一条线程处理事件。

我们从上到下把这四种类型解释一下:
POSTING中调用了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);
        }
    }

核心代码只有一句:subscription.subscriberMethod.method.invoke(subscription.subscriber, event);也很好理解,通过反射执行事件处理方法。

MAIN中首先判断当前线程是否是主线程,如果是,则执行invokeSubscriber,通过反射直接执行事件处理方法。反之则通过mainThreadPoster.enqueue(subscription, event);来执行事件的处理,那么它是如何切换线程的呢?答案很简单mainThreadPoster其实是一个主线程中的Handler,调用mainThreadPoster.enqueue(subscription, event);会触发它的handleMessage方法,在这个方法中最终调用的是eventBus.invokeSubscriber(pendingPost);。可以看到通过handler机制进行了线程的切换,在handleMessage中依旧是调用invokeSubscriber方法通过反射来执行事件处理方法。

BACKGROUND中首先判断是否在主线程,如果是则调用backgroundPoster.enqueue(subscription, event);backgroundPostermainThreadPoster不一样,backgroundPoster是一个Runnableenqueue实际上是开启了一个线程,在这个新的线程中再执行invokeSubscriber这个方法。如果不在主线程,则直接调用invokeSubscriber

ASYNCasyncPoster和上面提到的backgroundPoster一样,也是一个Runnable,它的enqueue实际上也是开启了一个线程,在这个新的线程中再执行invokeSubscriber这个方法。

至此为止post方法终于讲完了,我们来总结一下:

  • 将事件加入当前线程的事件队列
  • 遍历这个事件队列
    • 找到当前事件类型所有的父类事件类型,加入事件类型集合并遍历
      • 通过subscriptionsByEventType获取subscriptions集合,遍历这个subscriptions集合
        • 在POSTING、MAIN、BACKGROUND、ASYNC四种线程模式下通过反射执行事件处理方法

unregister

终于到最后一个方法了,unregister方法。不废话,看代码:

public synchronized void unregister(Object subscriber) {
    
        //获取订阅者所有订阅的事件类型
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            
            //遍历事件类型集合
            for (Class<?> eventType : subscribedTypes) {
                
                //从subscriptionsByEventType中删除订阅者的相关信息
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

这个方法的核心内容就是删除全局变量subscriptionsByEventType中所有包含当前订阅者的subscription,这也就很好的解释了为什么在解绑后我们不会再收到EventBus发送的事件了。因为发送事件的核心是根据事件类型从subscriptionsByEventType中获取subscriptions这个集合,遍历集合通过subscription可以拿到subscribersubscriberMethod,之后再通过subscription.subscriberMethod.method.invoke(subscription.subscriber, event);,采用反射来进行事件的处理。unregister方法删除了全局变量subscriptionsByEventType中所有包含当前订阅者的subscription,在遍历subscriptions的时候是不会获取包含当前订阅者的subscription,所以自然不会再收到事件。

观察者模式与EventBus

其实看完上面的分析,熟悉观察者模式的小伙伴们应该很清楚了,搞了这么多,其实EventBus不就是个观察者模式么。没错,EventBus的本质就是个观察者模式,常见的被观察者只有一个,而观察者的个数不确定。被观察者内部维持着一个观察者的集合,当被观察者要发送事件时会遍历内部的观察者集合,拿到观察者并调用观察者的相关方法。观察者的描述是不是和EventBus的整个流程非常一致?简而言之,EventBus就是一个被观察者,它的内部存放着一个subscriptionsByEventType集合,这个集合包含了我们所有的观察者,也就是调用了register的所有Activity或者Fragment等等。当使用post发送事件时,会遍历subscriptionsByEventType,拿到观察者,通过反射调用观察者中的事件处理方法。你没看错,EventBus的核心逻辑就是这么简单,仅仅只是一个观察者模式。

再多扯几句,有些小伙伴可能看完上面一大堆东西还是感觉不是很明白,没关系,很正常,谁也不太可能任何东西看一遍就了然于胸的。我的建议是可以打开Android Studio边看源码边看我的分析,不明白的跟进去看源码就好。

最后,希望大家能通过这篇分析加深对EventBus和观察者模式的理解,感谢大家。

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

推荐阅读更多精彩内容