美文网首页
EventBus源码学习

EventBus源码学习

作者: leilifengxingmw | 来源:发表于2019-02-16 13:55 被阅读1次

从16年开始做Android开发,就用过EventBus,到现在才刚刚知道其中的具体原理,哈哈。

EventBus版本3.1.1

implementation 'org.greenrobot:eventbus:3.1.1'
EventBus注册和发送事件.png

使用EventBus首先要进行注册。

EventBus.getDefault().register(this)

我们先来看看EventBus的getDefault方法

static volatile EventBus defaultInstance;

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

getDefault方法返回一个EventBus的单例对象。

EventBus的register方法

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

首先在register方法的注释1处调用findSubscriberMethods方法查找传入的订阅者的所有订阅方法

//METHOD_CACHE用来存储订阅者和订阅者的所有方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
//...
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //注释1处首先从缓存中查找方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        } 
        //注释2处,默认情况下ignoreGeneratedIndex是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 {
            //注释3处,把方法加入到缓存
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

在注释1处,先从缓存里面查找订阅者的所有订阅方法(例如Activity/Fragment中所有 @Subscribe注解的方法),如果找到就返回。
在注释2处,默认情况下ignoreGeneratedIndex是false,所以会调用findUsingInfo方法。

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
         //注释1处
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //注释2处
            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 {
                //注释3处
                findUsingReflectionInSingleClass(findState);
            }
            //注释4处
            findState.moveToSuperclass();
        }
        //注释5处
        return getMethodsAndRelease(findState);
    }

在注释1处我们构建了一个findState对象,并调用了findState的initForSubscriber方法。我们先来认识一下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) {
            //为FindState的subscriberClass和clazz赋值
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            //subscriberInfo为null
            subscriberInfo = null;
        }
        //...
}

调用了findState的initForSubscriber方法以后,findState的clazz不为null,findState的subscriberInfo为null。

所以会调用注释2处的代码
SubscriberMethodFinder的getSubscriberInfo方法。

private SubscriberInfo getSubscriberInfo(FindState findState) {
        //findState.subscriberInfo是null,这个条件不成立
        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;
                }
            }
        }
        //返回null
        return null;
    }

在默认subscriberInfoIndexes是null,所以getSubscriberInfo方法返回null,所以会调用findUsingInfo方法中注释3处的findUsingReflectionInSingleClass方法。

  private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // 获取类对象声明的所有方法,包括public, protected, default (package) access, and private methods,但是不包括继承的方法。
            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并且不是ABSTRACT,STATIC,BRIDGE,SYNTHETIC这几种类型
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                   //获取方法参数类型数组
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {//如果只有一种参数类型
                      //查找方法是否有Subscribe注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        //必须有@Subscribe注解
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //构建一个SubscriberMethod对象,并加入到findState.subscriberMethods中
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                }
                //...
            }
        }
    }

findUsingReflectionInSingleClass方法就是通过反射来获取订阅者中所有的方法,并根据方法的类型、参数和注解来找到订阅方法。找到订阅方法后即哪个订阅方法的相关信息保存在findSatate中。(有点疑问,在注册方法的时候我发现在kotlin中将方法的可见性声明为protected也可以订阅成功。可以看代码的逻辑只有public类型的方法才能注册成功。目前还没有找到原因。具体的例子在EventBusDemo中。如果有大佬知道,还请麻烦告知一下,哈哈。

接着回到findUsingInfo方法的注释4处,会继续查找订阅者父类中的订阅方法。

继续回到EventBus的register方法的注释2处,调用subscribe方法

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //订阅方法订阅的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        //注释1处
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //注释2处,查找是否存在eventType对应的Subscription对象列表
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {//如果不存在,就新建一个Subscription对象列表,并将eventType和对应的subscriptions加入到subscriptionsByEventType中
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //如果列表中存在和newSubscription一样的Subscription,则抛出异常
            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++) {
            //将newSubscription对象加入到subscriptions列表中,并且根据subscriberMethod的优先级
            //排序,默认情况下我们不考虑优先级。所以这里就是把newSubscription加入到subscriptions列表末尾
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //注释3处,查找订阅者对应的订阅事件类型列表,如果为null,则创建新的列表,并把eventType加入到列表中,
        //最后把subscriber和对应的subscribedEvents加入到typesBySubscriber中
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        //注释4处,理黏性事件
        if (subscriberMethod.sticky) {
            if (eventInheritance) {//eventInheritance默认是true
                // 必须考虑存在的eventType类型子类型的粘性事件
                //stickyEvents的Entry对象存储的是订阅事件类型和对应的事件
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    //如果eventType和candidateEventType是同一个类型,或者eventType是candidateEventType的父类或者父接口
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        //获取保存的事件,注意黏性事件是保存在内存中的,当你杀死进程以后,黏性事件就会消失。
                        Object stickyEvent = entry.getValue();
                        //注释5处,检查并发送黏性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

在subscribe方法的注释1处首先构建了一个Subscription对象。

final class Subscription {
    //订阅者
    final Object subscriber;
    //一个订阅方法
    final SubscriberMethod subscriberMethod;
    /**
     * 表示订阅者是否活动,当订阅者调用{EventBus#unregister(Object)} 方法的时候, 
     * active是false。在调用{@link EventBus#invokeSubscriber(PendingPost)} 方法的时候会检查订阅者的是否活动。
    */
    volatile boolean active;
//...
}

在subscribe方法的注释2处,查找是否存在eventType对应的Subscription对象列表,//如果不存在,就新建一个Subscription对象列表,并将eventType和对应的subscriptions加入到subscriptionsByEventType中。subscriptionsByEventType是EventBus类中的一个成员变量,用来存储订阅的事件类型和所有Subscription的映射。

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

在subscribe方法的注释3处,查找订阅者对应的订阅事件类型列表,如果为null,则创建新的列表,并把eventType加入到列表中,最后把订阅者和对应的订阅事件类型列表加入到typesBySubscriber中。typesBySubscriber是EventBus的一个成员变量,用来存储订阅者和对应的订阅事件类型之间的映射。

private final Map<Object, List<Class<?>>> typesBySubscriber;

在subscribe方法的注释4处,查找并处理粘性事件。eventInheritance默认是true,所以会调用注释5处的checkPostStickyEventToSubscription方法检查并发送粘性事件。

检查并发送黏性事件

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // 如果事件不为null,就发送
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

如果黏性事件不为null,则发送黏性事件

 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 MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(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是POSTING为例(默认订阅方法的threadMode就是POSTING,关于EventBus的线程模型本文没有分析,以后有需要再去分析),那么会调用invokeSubscriber方法

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

invokeSubscriber方法内部就是使用反射的方式调用订阅者的订阅方法。

注册方法就分析到这里,接下来看一看事件的发送。

EventBus.getDefault().post(MessageEvent("hello eventbus"))

EventBus的post方法

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        //注释1处,将事件加入到eventQueue
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);
        //当前没有在发送事件
        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                //注释2处,循环发送事件,事件发送是按照加入列表的顺序来发送的,先进先出。
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

在post方法的注释1处,先把先发送的事件加入到事件队列中。然后在注释2处,只要事件队列中有事件,就一直发送事件。

EventBus的postSingleEvent方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        //标记是否有该事件类型对应的订阅方法
        boolean subscriptionFound = false;
        //eventInheritance默认是true
        if (eventInheritance) {
            //获取eventClass对应的所有类,包括父类和父接口。
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                //注释1处
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        //注释2处
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            //注释3处sendNoSubscriberEvent 默认是true
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                //注释4处,post一个NoSubscriberEvent事件,最终只会在上面的注释2处打印日志然后结束
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

在postSingleEvent方法的注释1处,调用EventBus的postSingleEventForEventType方法

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
           //获取所有的订阅者
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        //存在事件对应的订阅方法就发送事件
        if (subscriptions != null && !subscriptions.isEmpty()) {
            //循环发送事件
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                   //调用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;
    }

如果存在事件对应的订阅方法就调用postToSubscription方法发送事件,在分析黏性事件的时候看过了,这里就不再看了。

我们回到在postSingleEvent方法的注释2处,如果没有找到事件对应的订阅方法,就打印一条日志。然后在注释3处如果条件符合,就会post一个NoSubscriberEvent事件。
我们举个事件没有对应的订阅方法的例子。
比如我们 post了事件A,但是我们没有声明订阅事件A类型的方法

  • 首先会在注释2处打印一条日志"No subscribers registered for event A"
  • 然后注释3处条件满足,我们递归post一个NoSubscriberEvent
  • 然后会在注释2处打印一条日志"No subscribers registered for event NoSubscriberEvent"
  • 这个时候,事件类型是NoSubscriberEvent了,不满足注释3处的条件,方法退出。

接下来看看EventBus的取消注册方法

 public synchronized void unregister(Object subscriber) {
     //获取订阅者订阅的所有事件类型列表
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            //注释1处
            unsubscribeByEventType(subscriber, eventType);
        }
        //注释2处
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

首先获取订阅者订阅的所有事件类型列表,然后在注释1处,调用unsubscribeByEventType方法移除订阅者对应的所有Subscription对象。

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

在注释2处,从typesBySubscriber中移除订阅者和对应的事件类型列表的映射

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 {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

遗留的问题:在注册方法的时候我发现在kotlin中将方法的可见性声明为protected也可以订阅成功。可以看代码的逻辑只有public类型的方法才能注册成功。目前还没有找到原因。具体的例子在EventBusDemo中。如果有大佬知道,还请麻烦告知一下,哈哈。
参考
[1]《Android 进阶之光》
[2]EventBus

相关文章

网友评论

      本文标题:EventBus源码学习

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