• EventBus使用详解*


    一、概述

    EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
    1、下载EventBus的类库
    源码:https://github.com/greenrobot/EventBus

    2、基本使用

    (1)自定义一个类,可以是空类,比如:

    public class AnyEventType {  
         public AnyEventType(){}  
     }  

    (2)在要接收消息的页面注册:

    eventBus.register(this);  

    (3)发送消息

    eventBus.post(new AnyEventType event);  

    (4)接受消息的页面实现(共有四个函数,各功能不同,这是其中之一,可以选择性的实现,这里先实现一个):

    public void onEvent(AnyEventType event) {}  

    (5)解除注册

    eventBus.unregister(this);  

    顺序就是这么个顺序,可真正让自己写,估计还是云里雾里的,下面举个例子来说明下。

    首先,在EventBus中,获取实例的方法一般是采用EventBus.getInstance()来获取默认的EventBus实例,当然你也可以new一个又一个,个人感觉还是用默认的比较好,以防出错。

    1)Android全局广播Broadcast

    首先全局广播是重量级别的,并且会消耗很多资源,但是可以跨进程通信,通过以上优缺点可以知道只有一种情况下才会用到它,跨进程的时候,这个特点也是其他方案不能达到的。

    2)Android本地广播Broadcast

    Local Broadcast也是会消耗很多资源的,但是相比全局广播要轻量一些,他的最大优点是可以拿到Context、Intent等和Android系统紧密相关的上下文,这样就方便数据的传递和接受。

    3)观察者模式

    观察者观察一个主题的时候,就会把自己添加到主题的List里面,然后主题更新的时候会遍历主题List中的观察者,调用观察者模式中的update函数来更新自己,最后离开的时候要从主题中移除观察者,防止引用造成的内存泄漏。网络上面观察者模式的例子,由此可以看出观察者模式中观察者是要实现主题更新时候的接口,其实这就是和EventBus的不同,耦合性比EventBus大。

    4)EventBus

    EventBus中执行

        EventBus.getDefault().register(this);
    

    的时候是让EventBus扫描当前类,把onEvent方法记录在Map里面,参数为key,方法为value,然后EventBus.getDefault().post(...)的时候,通过post里面的type来查找Map中对应的value,然后通过反射来执行我们的方法,最后别忘了在最后

    EventBus.getDefault().unregister(this); 
    

    注销EventBus防止内存泄漏。
    好多人说是观察者模式,但是仔细查看可以知道和观察者模式最大的不同是,主题在要更新数据的时候是通过反射来执行动作的
    个人感觉EventBus的缺点是:代码阅读性降低、不能跨进程、不能混淆,但是优点很多,比观察者模式耦合性更低,比广播更轻量。

    5)RxBus

    原理和EventBus一样,但是如果项目中用了RxJava,就可以选择这个强于EventBus.

    EventBus还有另外有个不同的函数,他们分别是:

    1、onEvent
    2、onEventMainThread
    3、onEventBackgroundThread
    4、onEventAsync

    这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同,在介绍不同之前先介绍两个概念:
    告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布,观察者被告知事件发生叫做事件的接收,是通过下面的订阅函数实现的。

    onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
    onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
    onEventBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
    onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

     

    1. EventBus.getDefault();开始
    入口EventBus.getDefault();开始,这里我还是很喜欢这样的静态单例对象获取方法名,我本人也很喜欢使用单例的时候,取名getDefault()。
    /** Convenience singleton for apps using a process-wide EventBus instance. */
        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;
        }
    构造方法中创建了几个容器,用来装各种订阅者信息的,很关键的几个成员我们需要注意,mainThreadPoster 、backgroundPoster 、asyncPoster ,这个几个是后来post消息的时候,用的到的,就是利用他们将消息在不同的线程发送出去,达到在不同线程中执行的效果。
    mainThreadPoster:很显然是一个自定义扩展后的Handler,在构造的时候传入的是主线的Looper,所以它是一个主线程的对应的Handler。
    这里之间先看看这个Handler的代码吧:
     * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
    package org.greenrobot.eventbus;
     
    import android.os.Handler;
     
    final class HandlerPoster extends Handler {
     
        private final PendingPostQueue queue;
        private final int maxMillisInsideHandleMessage;
        private final EventBus eventBus;
        private boolean handlerActive;
     
        HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
            super(looper);
            this.eventBus = eventBus;
            this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
            queue = new PendingPostQueue();
        }
     
        void enqueue(Subscription subscription, Object event) {
            PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
            synchronized (this) {
                queue.enqueue(pendingPost);
                if (!handlerActive) {
                    handlerActive = true;
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                }
            }
        }
     
        @Override
        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;
            }
        }
    } 
    没错,就是传入一个MainThread的Looper,创建了一个主线程的Handler,毋庸置疑,通过他发送的事件消息,都是在主线中执行的。

    backgroundPoster : 那这个是什么呢,我们来看看代码。
     * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
    package org.greenrobot.eventbus;
     
    import android.util.Log;
     
    /**
     * Posts events in background.
     * 
     * @author Markus
     */
    final class BackgroundPoster implements Runnable {
     
        private final PendingPostQueue queue;
        private final EventBus eventBus;
     
        private volatile boolean executorRunning;
     
        BackgroundPoster(EventBus eventBus) {
            this.eventBus = eventBus;
            queue = new PendingPostQueue();
        }
     
        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);
                }
            }
        }
     
        @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;
            }
        }
    }
    很明显他是一个实现了Runnable 的对象,所以它的载体肯定是一个子线程,是给某子线程执行具体任务的。
     
    asyncPoster :再来看看这个,这个听名字就感觉已经知晓大部分信息了,异步Poster,可定也是在单独线程中post任务的。
    class AsyncPoster implements Runnable {
     
        private final PendingPostQueue queue;
        private final EventBus eventBus;
     
        AsyncPoster(EventBus eventBus) {
            this.eventBus = eventBus;
            queue = new PendingPostQueue();
        }
     
        public void enqueue(Subscription subscription, Object event) {
            PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
            queue.enqueue(pendingPost);
            eventBus.getExecutorService().execute(this);
        }
     
        @Override
        public void run() {
            PendingPost pendingPost = queue.poll();
            if(pendingPost == null) {
                throw new IllegalStateException("No pending post available");
            }
            eventBus.invokeSubscriber(pendingPost);
        }
    }
    所以这里就不用多说了,也是一个实现了Runnable 的对象,一定也是被子线程执行的具体任务。
     
    所以最后我们都能看到上面三段代码两个共同的两行代码:
    eventBus.invokeSubscriber(pendingPost);  这行代码其实就是在使用反射调用订阅的者的事件方法。
     eventBus.getExecutorService().execute(this); 这行代码很明显就是把时间提交给一个空闲的线程去执行。
    当然HandlerPoster 就除外了,当然他是使用sendMessage的方式去去执行一个具体的事件,其实对应的也是 eventBus.getExecutorService().execute(this);这行代码。
     
    OK,这里我们就浅层次的分析到这里,我们回到主线,继续。
     
     
    2.register(Object subcriber)注册订阅者
     
    我们通过EventBus的getDefualt()方法获取了一个EventBus的对象,然后就可以通过他来注册地订阅者了,接下来就看看EventBus的register(Object subcriber)方法是如果实现的呢?
        public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    OK,从上面的代码中,我们可以看到调用了subscriberMethodFinder对象的findSubscriberMethods(subscriberClass);方法,subscriberMethodFinder这个对象呢,我们其实都不用太关心,可以把他当作一个管理获取、查找、订阅者和订阅者Method的对象,然后通过它我们就能拿到一个List集合,这个集合存储的都是SubscriberMehtod对象,这里有必要去追求一下SubscriberMehtod是个什么东西呢,我们来看看吧:
     * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
    package org.greenrobot.eventbus;
     
    import java.lang.reflect.Method;
     
    /** Used internally by EventBus and generated subscriber indexes. */
    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;
        }
     
        @Override
        public boolean equals(Object other) {
            if (other == this) {
                return true;
            } else if (other instanceof SubscriberMethod) {
                checkMethodString();
                SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
                otherSubscriberMethod.checkMethodString();
                return methodString.equals(otherSubscriberMethod.methodString);
            } else {
                return false;
            }
        }
     
        private synchronized void checkMethodString() {
            if (methodString == null) {
                // Method.toString has more overhead, just take relevant parts of the method
                StringBuilder builder = new StringBuilder(64);
                builder.append(method.getDeclaringClass().getName());
                builder.append('#').append(method.getName());
                builder.append('(').append(eventType.getName());
                methodString = builder.toString();
            }
        }
     
        @Override
        public int hashCode() {
            return method.hashCode();
        }
    }
    很明显从这个SubscriberMethod 类的成员变量就能看出来,这是一个封装了一个关于Method的信息的对象,这些信息包括  final Method method(反射方法对象)对象、 final ThreadMode threadMode(线程模式)对象、final Class<?> eventType(事件类型,方法中的参数)对象、final int priority(权限) 标记、  final boolean sticky(是否sticky类型)标记、 String methodString(方法描述字串) 用来提高两个SubscriberMethod对象比较效率和准确性的。
    所以这类就这么简单,就是封装了一个Mehtod的全部信息的。
     
    接下来代码主线往下走。
    我们来看一下findSubscriberMethods方法,是如何获取到订阅者的所有方法信息的,直接看方法。
     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;
            }
        }
     
    从上面的代码可以看的出,一开始METHOD_CACHE这个集合是空的,获取的时候为空,逻辑会往下走,ignoreGeneratedIndex经过打印默认初始是为false的,所以下一步会走subscriberMethods = findUsingInfo(subscriberClass); OK,直接来看findUsingInfo(subscriberClass);方法。
      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);
        }
    findState.subscriberInfo = getSubscriberInfo(findState);根据打印,获取到的为null,
    所以会走  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) {
                methods = findState.clazz.getMethods();
                findState.skipSuperClasses = true;
            }
            for (Method method : methods) {
                int modifiers = method.getModifiers();
         if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) {
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        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");
                }
            }
        }
    所以通过上面的代码,很明显就可以看出,是通过反射拿到每一个方法的修饰符信息,注解信息,参数信息,从上面的代码很明显就知道,我们的订阅方法必须有且只有一个参数,否则会抛出异常的,而且订阅方法必须是public修饰的,而且不是静态、不是抽象方法,否者也会抛出异常的,最后将合法的订阅方法都存到了一个findState.subscriberMethods集合中了,最后在findUsingReflectionInSingleClass(findState);的调用方发处调用了 
    return getMethodsAndRelease(findState);返回了出去了一个List<SubscriberMethod>集合,这里需要稍微提醒一个地方就是,我们应该都发现了findUsingInfo(Class<?> subscriberClass) 方法中的倒数第四行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;
                    }
                }
            }
    这个方法主要是获取当前Class对象的父类(有可能是抽象类或者接口),这样在findUsingInfo(Class<?> subscriberClass) 方法while中循环的获取到每一级Class对象中所有方法信息。
     
    OK,findSubscriberMethods(...)方法就分析到这里,主线接着往下走,回到register方法中,往下看。
     
    然后我们能看到一个for循环的遍历。
     synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
    遍历每一个SubscriberMethod对象,遍历到每一个SubscriberMethod对象之后,调用EventBus的 subscribe(subscriber, subscriberMethod);方法,那我们就直接来看这个方法。
     // Must be called in synchronized block
        private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            Class<?> eventType = subscriberMethod.eventType;
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            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();
            for (int i = 0; i <= size; i++) {
                if (i == size || subscriberMethod.priority > 
    subscriptions.get(i).subscriberMethod.priority) {
                    subscriptions.add(i, newSubscription);
                    break;
                }
            }
     
            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,然后就创建了一个Subscription对象,这里又需要解释一下这个对象是个什么玩意了,直接贴代码吧。
     * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
    package org.greenrobot.eventbus;
     
    final class Subscription {
        final Object subscriber;
        final SubscriberMethod subscriberMethod;
        /**
         * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
         * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
         */
        volatile boolean active;
     
        Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
            this.subscriber = subscriber;
            this.subscriberMethod = subscriberMethod;
            active = true;
        }
     
        @Override
        public boolean equals(Object other) {
            if (other instanceof Subscription) {
                Subscription otherSubscription = (Subscription) other;
                return subscriber == otherSubscription.subscriber
                        && subscriberMethod.equals(otherSubscription.subscriberMethod);
            } else {
                return false;
            }
        }
     
        @Override
        public int hashCode() {
            return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
        }
    }
    其实就是很简单的一个对象,我们可以看做一个简单的java bean就好了,他的功能就是封装了一个订阅者实体对象( final Object subscriber;)和上面我们解释过的,一个Mehtod信息的封装类(final SubscriberMethod subscriberMethod;)。
     
    继续回到主线,我们可以看到,会把EventType和Subscriptions关联起来,很好理解,可以想象的到,我们Post一个事件的时候,一般会带一个事件类型,也就是post方法中的参数。
    而一个EventType可能对应很多个方法和对象,然后就把他们存储起来了,源代码体现如下:
     Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            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);
                }
            }
    然后把一个个的新new出来的Subscription添加了subscriptions中,而subscriptions又和evetType关联起来了。添加新new出来的Subscription的时候,它会根据事件方法当时声明的时候,设置的priority注解信息,当priority的放在List的队列前面。
     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;
                }
            }
    再往下走,就是用实体对象和EventType关联了,这个也不难理解,我们可以理解为,一个实体对象可以写很多个订阅方法,但是没一个订阅方法可能会是不同的EventType(订阅方法的参数类型),所以最后把eventType添加到了subscribedEvents之中。
     List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            subscribedEvents.add(eventType);
    最后一部分的代码是处理Sticky事件的逻辑,我们知道EventBus是可以调用成员方法
    来postSticky(Object event)方法来提交一个事件。主要判断subscriberMethod的sticky是否为true,如果是,就从stickyEvents集合中获取所有相同事件类型的且设置了sticky属性的订阅者和订阅方法,然后就调用checkPostStickyEventToSubscription(newSubscription, stickyEvent);方法将事件发送出去了。
     
    OK,到这里基本上register模块都解释的差不多了,上面我们也看到,各种事件对象、方法对象、和订阅者对象都已经封存起来,可以等待post事件的时候,将事件发送出去。
    3. post(Object event) 发布订阅事件

    Ok我们继续往下分析关于事件post的时候的实现原理。
    我们知道我们在拿到EventBus的实例单例对象的时候,可以简单的就调用的他的post刚发,将一个特定的订阅事件类型发送出去,订阅者的订阅方法就能被回调了,我们来直接看这个方法吧。
     /** Posts the given event to the event bus. */
        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;
                }
            }
        }
    首先从currentPostingThreadState(LocalThread)中获取一个与当前线程关联的PostingThreadState对象,这个对象很简单,其实就是一个记录一个线程事件post状态的实体,可以稍微看看他的代码。
     /** For ThreadLocal, much faster to set (and get multiple values). */
        final static class PostingThreadState {
            final List<Object> eventQueue = new ArrayList<Object>();
            boolean isPosting;
            boolean isMainThread;
            Subscription subscription;
            Object event;
            boolean canceled;
        }
    没错就是这么简单,其成员也很好理解,eventQueue 很明显就是一个事件队列,isPosting 这个boolean类型的标记只要就是记录当先线程的事件发布状态,isMainThread主要就是标记当前线程是否为主线程,event事件类型、canceled是否取消post。
     
    首先判断状态,假如下载正在post事件的时候,就不在接受post其他事件了,我们直接分析if语句块里面的代码吧,如果事件队列为空的话,就结束post过程,方法执行结束。
    否则进入postSingleEvent(eventQueue.remove(0), postingState);方法中,然后每次都是获取事件队列中的栈顶的一个事件,注意是使用的remove(0)的方式来获取,直接来看postSingleEvent(Object event, PostingThreadState postingState)方法吧。
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            Class<?> eventClass = event.getClass();
            boolean subscriptionFound = false;
            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的,所以会先调用llookupAllEventTypes(eventClass)获取eventClass所有父类的后者接口类型的class对象,存到eventTypes 集合中,这里就不贴出lookupAllEventTypes(eventClass)的代码实现了,因为他的代码很简单,主要就是想上获取父类或者接口类型的class对象,因为比如父类或者接口为Car,BMW extends Car,这些都会被获取出来,记住是eventClass的父类和接口的calss对象哦,然后挨个遍历evnetClass对象。
    for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                 subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
    }
    然后调用了postSingleEventForEventType(event, postingState, clazz);方法,我们直接看这个方法。
    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(subscription, event, postingState.isMainThread);
                        aborted = postingState.canceled;
                    } finally {
                        postingState.event = null;
                        postingState.subscription = null;
                        postingState.canceled = false;
                    }
                    if (aborted) {
                        break;
                    }
                }
                return true;
            }
            return false;
        }
    OK,我们看到,从subscriptionsByEventType中取出一个subscriptions 对象,我们应该还有印象,前面我们讲过,说事件类型和事件订阅者关联起来了,一个订阅者可以实现多个订阅方法,接收不同事件类型,所以事件类型和订阅者关联起来, 将所有订阅者保存成集合,和事件类型关联了,Ok遍历subscriptions ,然后调用postToSubscription(subscription, event, postingState.isMainThread);方法去post事件给订阅者。我们直接看这个方法。
     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);
            }
        }
    所以上面的代码已经很明显了,就是根据ThreadMode来在不同的线程中发布事件,第一种  case POSTING:是默认方式下的,表示在当前线程中执行,也就是在事件post所在线程中执行,我们看到它是直接调用 invokeSubscriber(subscription, event)方法去执行了,看看这个方法吧。
     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);
            }
        }
    我们看到,代码很简单,就一句话,利用发射的方式去执行了,其他的都是异常捕获。
     
    接着看第二种  case MAIN:很明显是在主线程中执行的,我们看这个case下的实现方式。
    if (isMainThread) {
                        invokeSubscriber(subscription, event);
                    } else {
                        mainThreadPoster.enqueue(subscription, event);
      }
    很好理解,如果当前已经是在主线程中,就直接跟第一种方式一样,直接在当前线程中执行就OK了,如果当前不是主线程,那么只能通过主线程的Handler来发送handler事件消息到主线程去执行了,使用mainThreadPoster将事件加入执行队列,mainThreadPoster我们在一开始的时候我们讲过,他们的实现也很简单,这个具体怎么发送和执行可以去看看,但是他们最后都是调用EventBus的invokeSubscriber(subscription, event);方法来执行的。
     
    第三种case BACKGROUND: 很明显,就是在后台子线程中执行了,看看这个case下的代码实现吧。
    if (isMainThread) {
                   backgroundPoster.enqueue(subscription, event);
               } else {
                   invokeSubscriber(subscription, event);
        }
    也很简单,直接判断当前是否为主线程,如果主线程的话,就直接使用backgroundPoster来将事件加入执行队列, 
    backgroundPoster我们在前面做过铺垫,他就是一个Runnable的实现体,接着调用了它自己的enqueue方法,我们稍微提一下这方法。
     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);
                }
            }
        }
    最后线程执行,就调用了 
    backgroundPoster的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;
            }
        }
    他最后是通过queue.poll(1000);取出一个事件,将事件交个了EventBus中线程池来执行,从而达到了 
    backgroundPost的效果,也就是子线程执行的效果。
     
     
    但是我们这里应该需要注意一个地方,那就是
     
    queue.enqueue(pendingPost);这个将事件的入队操作,这里还是有必要看一下queue这个队列是个什么样的实现方式。
    final class BackgroundPoster implements Runnable {
     
        private final PendingPostQueue queue;
        private final EventBus eventBus;
     
        private volatile boolean executorRunning;
     
        BackgroundPoster(EventBus eventBus) {
            this.eventBus = eventBus;
            queue = new PendingPostQueue();
        }
     
        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);
                }
            }
        }
    我们看到queue 是一个PendingPostQueue类型的实体成员变量。我们直接贴上这个类的代码来看看。
    * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
     
    package org.greenrobot.eventbus;
     
    final class PendingPostQueue {
        private PendingPost head;
        private PendingPost tail;
     
        synchronized void enqueue(PendingPost pendingPost) {
            if (pendingPost == null) {
                throw new NullPointerException("null cannot be enqueued");
            }
            if (tail != null) {
                tail.next = pendingPost;
                tail = pendingPost;
            } else if (head == null) {
                head = tail = pendingPost;
            } else {
                throw new IllegalStateException("Head present, but no tail");
            }
            notifyAll();
        }
     
        synchronized PendingPost poll() {
            PendingPost pendingPost = head;
            if (head != null) {
                head = head.next;
                if (head == null) {
                    tail = null;
                }
            }
            return pendingPost;
        }
     
        synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
            if (head == null) {
                wait(maxMillisToWait);
            }
            return poll();
        }
     
    }
    从上面的代码我们可以看出,poll和enqueue 都加上synchronized ,他就是模拟了一个事件队列,enqueue就是将事件入队的动作,当然还模拟了一个事件的队列的队头队尾和next,首先上面代码一开始head为和tail应该都为null,所以会执行如下代码。
                head = tail = pendingPost;
    将当前队列的头尾都设置为pendingPost(当前事件),然后调用notifyAll()方法,通知线程去执行任务,就是把任务交给EventBus中声明的线程池来执行,最后调用到了run方法了,在run方法中调用了 queue.poll(1000);来一个事件,看看poll(int maxMillisToWait)方法吧。
     synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
            if (head == null) {
                wait(maxMillisToWait);
            }
            return poll();
        }
    也是很简单啊,直接判断队头是否为空,如果为空就让线程先等1000毫秒,所以最后只有在enqueue被调用,加入新的事件后,notifyAll唤醒线程去调用无参数的重载方法来取出事件返回出去。
    synchronized PendingPost poll() {
            PendingPost pendingPost = head;
            if (head != null) {
                head = head.next;
                if (head == null) {
                    tail = null;
                }
            }
            return pendingPost;
        }

     方法也很简单,首先直接把head赋值给一个新的 pendingPost变量,再判断如果head不为空,直接把下一个next事件复制给head,典型的先进先出队列,然后返回pendingPost。

     
    其实到这里其实也有两个很重要的知识点,队列概念的巧妙是使用,先进先出方式的使用,还有一个是生产者和消费者的概念。
    回归主线,判断如果不是主线程,就刚好,直接执行事件就可以了,看最后一种
    case ASYNC:
    这种其实跟 BACKGROUND一样的执行方式,因为asyncPoster本身也是一个Runnable的实现体,最后也调用了enqueue方法把任务交给了EventBus中的线程池来执行,具体代码基本和BACKGROUND的分析一样的。但是这个和BACKGROUND是有区别的,它是任何时候都要在子线程中执行的.
  • 相关阅读:
    退休夫妇不顾反对坚持创业,把自己的品牌推向了市场
    年终将至,财务人如何做好数据分析?
    圣诞快乐:Oracle Database 19c 的10大新特性一览
    perl 获取表记录数
    rex 防止调度还没完成后又继续发起
    希腊女孩创办自媒体教希腊语,如今用户已达1000人
    在20天里赚三千多元,他靠创意经营商店,扩大了店面
    小杂货店的崛起,他坚信创新和拼搏是成功的两大法宝
    thinkphp
    thinkphp
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/6087506.html
Copyright © 2020-2023  润新知