Android开发Android知识

EventBus3.0源码分析(二)post

2017-04-16  本文已影响57人  static_sadhu

距离上篇整整两个月了,忙不是借口,反省。

上一篇文章介绍了register的过程,这篇接着来,一起看看EventBus发送事件的过程。EventBus可以通过post发送普通事件,还可以通过postSticky发送粘性事件。粘性事件与普通事件的区别在于:当粘性事件发布后,若有订阅者订阅该事件,该订阅者依然能收到最近一个该事件, 这点在上篇最后有提到。有了这个最基本的认识,我们来看看这两个方法的具体实现。

先瞅瞅发送普通事件post方法的源码

public void post(Object event) {
    // 获取当前线程的PostingThreadState
    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;
        }
    }
}

简单说一下post的流程:先获取当前调用post方法的PostingThreadState,并将该事件添加到事件队列中,如果当前没有发送事件, 则通过postSingleEvent方法将队列中的事件一一发送出去。

继续分析postSingleEvent方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    // 获取该事件的class
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
  // 是否支持事件继承(默认是支持的)
    if (eventInheritance) {
        // 查找所有跟eventClass相关的类(包括eventClass实现的接口以及父类)
        // 若是第一次发送eventClass,通过api获取其父类及其实现接口的集合,并放入map中缓存,
       //  第二次直接从map中取
        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) {
        // 默认情况下, 如果没有订阅者订阅该事件, 则打印如下信息,并且发送NoSubscriberEvent事件
        if (logNoSubscriberMessages) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
 
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

继续简单的说下上述postSingleEvent过程: 先判断是否支持事件继承,(默认情况下支持)如果支持,则寻找此事件对应类所有的父类,以及实现的接口,并发送。如果不支持,则仅发送此事件。 换句话说,如果支持事件继承, 订阅者订阅该事件的父类,或者其实现的接口,都能接受到该事件。写了个小demo大家看看:child 继承parent,并且实现了interfaceAinterfaceB

public class Child extends Parent implements interfaceA, interfaceB {
}

// 发送child事件,则订阅了其父类或者其实现接口的订阅者,都能接受child事件
@Subscribe(threadMode = ThreadMode.MAIN)
 public void onInterfaceAEvent(interfaceA a) {
    Log.i(TAG, "onInterfaceAEvent: " + a.toString());
 }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onInterfaceBEvent(interfaceB b) {
    Log.i(TAG, "onInterfaceBEvent: " + b.toString());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onParrentEvent(Parent p) {
    Log.i(TAG, "onParrentEvent: " + p.toString());
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onChildEvent(Child c) {
    Log.i(TAG, "onChildEvent: " + c.toString());
}

postSingleEvent中真正发送事件的方法是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(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

上述方法主要取得订阅了eventClass的订阅者集合,并且通过postToSubscription在指定的线程回调订阅方法:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
  // 订阅者的订阅方法需要在哪个线程调用
    switch (subscription.subscriberMethod.threadMode) {
        // 在调用post的线程,则直接调用订阅方法
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            // 需要在主线程回调,并且调用post的线程也是主线程,则直接调用订阅方法
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                 // 需要在主线程回调,调用post的线程不是主线程,则通过mainThreadPoster切换到主线程
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case BACKGROUND:
             // 需要在非主线程回调,调用post的线程也是主线程,则通过backgroundPoster切换到子线程
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                 // 需要在非主线程回调,并且调用post的线程也是非主线程,则直接调用订阅方法
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            // 不论调用post的线程是否为主线程,都使用一个空闲线程来处理
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

至此,整个post过程也就结束了。

至于postSticky只是比post多了一个步奏,在发布事件之前,先将该事件放入粘性事件集合stickyEvents(事件类型作为key,具体事件作为value)中,当有订阅者订阅该事件并且注明接受粘性事件看,则能接收到最近的一个该事件。

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        stickyEvents.put(event.getClass(), event);
    }
    // Should be posted after it is putted, in case the subscriber wants to remove immediately
    post(event);
}

最后借用网络上的一幅图来总结整个post流程:

post流程
上一篇下一篇

猜你喜欢

热点阅读