EventBus3.0源码分析(二)post

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

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

推荐阅读更多精彩内容