EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)

EventBus源码分析(一):入口函数提纲挈领(2.4版本)
EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
EventBus源码分析(四):线程模型分析(2.4版本)
EventBus源码解读详细注释(1)register的幕后黑手
EventBus源码解读详细注释(2)MainThread线程模型分析
EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别
EventBus源码解读详细注释(4)register时刷新的两个map
EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义
EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!

EventBus维护了一个重要的HashMap,这个HashMap的键是事件,值是该事件的订阅者列表,因此post事件的时候就能够从此HashMap中取出事件的订阅者列表,对每个订阅者反射调用事件处理方法。

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

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

因为EventBus发布事件的线程多样性,可能是主线程,可能是线程池分配的线程,也可能是临时非配的一个线程。EventBus发布事件和处理事件可能会线程切换,因此需要记录发布者线程的状态。

PostingThreadState:记录发布者线程状态

PostingThreadState类正是记录了发布者的状态

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

可见PostingThreadState为给个线程设置了一个事件队列,一个事件,一个封装好的订阅者Subscription对象和三个标志位(最主要的信息就是发布者线程是否为主线程,随后会根据发布者线程是否为主线程决策是否要线程切换)。
Java中为每个线程设置不同的数据使用到的是ThreadLocal,EventBus通过ThreadLocal为每个线程创建一个PostingThreadState对象并保存发布者的状态。

    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

post方法中首先获取了发布者线程的PostingThreadState对象,获取该线程对象的事件队列,并把这次发布的事件添加到发布者线程的事件队列中。

        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

然后通过循环,取出发布者线程的事件队列的每一个事件,进行发布,一直到队列为空。
如果正在循环处理事件队列,进行事件发布,那么发布者线程的isPosting属性为真,这时候再次调用post发布事件,只会进队列,不会再次循环,因为之前的循环会处理队列中的事件,包括后来入队列的事件。事件队列此时为一个生产者消费者模型,EventBus消费事件,用户通过post生产事件。

   postingState.isPosting = true;

因此post发布事件的时候,会先获取发布者线程的信息(最重要的线程信息是发布者线程是否为主线程isMainThread),然后对发布者线程发布的每一个事件进行排队,通过postSingleEvent方法对队列中的每一个事件进行处理,postSingleEvent方法需要知道发布者线程是否为主线程,通过传入发布者线程的ThreadLocal的PostingThreadState对象即可。

postSingleEvent:事件继承性分析

如果post(A),A extends B implements C
那么onEvent(A)、onEvent(B)、onEvent(C)这三个个事件处理方法那些能得到调用呢
答案是onEvent(A)、onEvent(B)、onEvent(C)这三个事件处理方法都会得到调用。
EventBus有个属性eventInheritance专门用来控制是否要处理事件的父类,默认值是true,表示默认是要处理事件父类的。
postSingleEvent对是否要处理事件的父类进行了分类讨论。

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

如果不需要处理事件父类的话,那么直接调用postSingleEventForEventType对当前的事件进行发布处理即可。
否则,需要找到当前发布事件的所有父类(包括实现的接口),对父类也通过postSingleEventForEventType方法进行处理。

如何获取一个类的所有父类和实现的接口

    ...
        private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();
    ...
    /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
    private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<Class<?>>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

EventBus对于缓存的设计是非常的精辟准确。获取一个类的所有的父类和实现的接口这一操作,是比较耗费性能的,为了更快的获取,采用缓存策略,以空间换时间,缓存同样通过静态的HashMap实现。
lookupAllEventTypes方法中同样是先获取缓存,只有在缓存不命中的情况下才进行查找,查找的过程就是向上循环迭代,遇到根类Object停止迭代 。
对应所实现的接口的处理,则是对每一层,进行递归获取

    /** Recurses through super interfaces. */
    static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
        for (Class<?> interfaceClass : interfaces) {
            if (!eventTypes.contains(interfaceClass)) {
                eventTypes.add(interfaceClass);
                addInterfaces(eventTypes, interfaceClass.getInterfaces());
            }
        }
    }

postSingleEventForEventType:获取事件的订阅者列表,对每个订阅者进行事件处理

最开始EventBus维护的事件到订阅者列表的map终于派上用场了,从该map中获取待发布事件的所有订阅者,对每个订阅者都要进行事件处理。

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

postSingleEventForEventType方法首先在同步代码块中获取事件的订阅者列表

        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }

对每一个订阅者进行处理的方法需要传入一个发布者线程的信息,其实EventBus只关心发布者线程是否为主线程,因为根据不同的线程模型可能会线程切换

 for (Subscription subscription : subscriptions) {
     ...
     postToSubscription(subscription, event, postingState.isMainThread);
     ...
  }

postToSubscription:线程切换

在postToSubscription方法中,EventBus根据事件的线程模式和发布者线程是否为主线程,在规定的线程中对事件处理方法进行反射调用

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }
  1. PostThread模式不需线程切换,直接在发布者线程进行事件处理。
  2. MainThread模式分类讨论:发布者线程是主线程则直接调用事件处理方法,否则通过Handler进行线程切换。
  3. BackgroundThread模式分类讨论:发布这线程不是主线程则在发布者线程直接处理事件,否则线程切换至线程池处理。
  4. Async模式不关心发布者线程直接在线程池中开辟一个新的线程处理事件。
    具体的线程模型分析将会在下一篇进行详细分析。

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方法对事件处理方法进行了反射调用。invoke方法会可能会抛出三个异常,这里只处理了两个。IllegalArgumentException异常并没有捕获,因为在保存事件处理方法的时候已经做了约束,保存的事件处理方法肯定只有一个参数,因此肯定不会抛出非法参数异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值