EventBus源码解析(二)———EventBus事件发送

本文深入剖析了EventBus的工作原理,详细解读了事件的发送流程,包括post方法的执行过程,以及不同线程模式下的事件处理策略。同时,探讨了黏性事件的发送机制和EventBus性能优化的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章列表

EventBus源码解析(一)
EventBus源码解析(二)
EventBus源码解析(三)

第一篇文章中我们讲到了事件的注册,本篇文章讲解,事件是如何发送出去的。EventBus的post方法系列。

Post方法

/** Posts the given event to the event bus. */
public void post(Object event) {
	//PostingThreadState 存储“发送事件“这个动作的一些信息,这个封装后的集合会存储在线程本地变量中,以防多线程操作时线程之间数据的污染
    PostingThreadState postingState = currentPostingThreadState.get();
   	//将要发送的时间存进postingState的eventQueue变量中
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
	//postingState.isPosting是否正在发送、默认为false
    if (!postingState.isPosting) {
   		//检查当前线程是否运行在主线程
        postingState.isMainThread = isMainThread();
        //修改isPosting为正在发送的状态
        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;
        }
    }
}

核心代码就是postSingleEvent(eventQueue.remove(0), postingState),看下这个方法都做了哪些工作

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //是否考虑继承、默认是true
    if (eventInheritance) {
    	//查找eventType继承的类和接口
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        //遍历这些类和接口以及eventType类自己组成的list
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            //分别调用postSingleEventForEventType并与subscriptionFound取"或"操作,然后赋值给subscriptionFound
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

看一下方法lookupAllEventTypes

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

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

这个方法就是获取eventType的父类和它继承的接口,把这些都存入到list中,并返回,返回之前将list存进一个以eventType为Key的map中缓存起来。再回到postSingleEvent方法中,遍历返回的list分别调用postSingleEventForEventType(event, postingState, clazz)方法,下面看一下这个方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
    	//看到上一篇文章中出现的变量了、根据eventType的class对象取出订阅者信息的对象
        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;
}

看一下这个方法中就是遍历订阅者,分别调用postToSubscription(subscription, event, postingState.isMainThread)方法

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

看到上一篇文章结尾的那个方法,在上一节已经介绍过了invokeSubscriber方法。信息发送完成返回到方法postSingleEventForEventType中,订阅当前eventType的所有的订阅方法的都已完成调用,返回true,回到前一个方法postSingleEvent。因为是在eventType的 父类及接口集合中,调用的发送信息的方法、所以循环结束,订阅eventType及其父类和接口的事件的方法都已完成调用,看下subscriptionFound,如果没有一个订阅方法订阅该事件,则此变量为false。会执行后续的代码,否则不会执行,后续代码就是打印log并发送一个“无订阅者订阅”的事件。
从上一篇文章到本篇文章,只是把最基本的订阅发送流程讲通了,还有一些其他的细枝末节没讲到。下面会针对没讲到的查漏补缺。

postSticky方法

发送黏性事件的时候,就不能调用post方法了。而是调用postSticky,下面我们看一下发送黏性事件的流程:

/**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
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);
}

变量stickyEvents用来存储事件,当需要黏性监听方法处理的时候(即我们上一篇文章中,subscribe方法的末尾有针对黏性监听事件的处理逻辑),就会从这里取事件。然后调用post方法,走回之前的逻辑。

另外我们看一下postToSubscription方法针对其他ThreadMode的订阅方法是如何处理的。

postToSubscription方法:

代码不贴了,看上面的代码就好,不同的模式分别介绍
POSTION模式:已经介绍过了。
MAIN模式:此模式下分为两个逻辑,1)如果当前线程为主线程就执行invokeSubscriber方法,和POSTION模式一致;2)如果当前不为主线程,则执行mainThreadPoster.enqueue(subscription, event);这句代码,那mainThreadPoster这是什么呢?
回过头来先看几个变量:

// @Nullable
private final MainThreadSupport mainThreadSupport;
// @Nullable
private final Poster mainThreadPoster;
//..省略...
EventBus(EventBusBuilder builder) {
   //...省略..
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    //...省略..
}

找到mainThreadPoster定义及初始化的地方,跟进去看看这两个变量是从哪来的,先看EventBusBuildergetMainThreadSupport方法

MainThreadSupport getMainThreadSupport() {
     if (mainThreadSupport != null) {
     	return mainThreadSupport;
     } else if (Logger.AndroidLogger.isAndroidLogAvailable()) {//这里找能到'android.util.log'类,所以返回值true
     	//这里调用下面的方法,其实就是获取AndroidUI线程的Looper对象
        Object looperOrNull = getAndroidMainLooperOrNull();
        //看这里。会返回一个MainThreadSupport的内部类AndroidHandlerMainThreadSupport的对象
        return looperOrNull == null ? null :
                new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
    } else {
        return null;
    }
}

Object getAndroidMainLooperOrNull() {
    try {
        return Looper.getMainLooper();
    } catch (RuntimeException e) {
        // Not really a functional Android (e.g. "Stub!" maven dependencies)
        return null;
    }
}

上面注释写的很清楚getMainThreadSupport方法最终返回了AndroidHandlerMainThreadSupport对象,回到EventBus的构造方法中,mainThreadSupport其实就是MainThreadSupport类的内部类AndroidHandlerMainThreadSupport的对象,这个对象内部持有AndroidUi线程的Looper的引用。
所以mainThreadPoster变量其实就是AndroidHandlerMainThreadSupport对象调用createPoster方法的返回值,而这个方法返回的就是一个handler,也就是说mainThreadPoster其实就是一个handler对象,
贴一下MainThreadSupport类的代码

public interface MainThreadSupport {

   boolean isMainThread();

   Poster createPoster(EventBus eventBus);

   class AndroidHandlerMainThreadSupport implements MainThreadSupport {

       private final Looper looper;

       public AndroidHandlerMainThreadSupport(Looper looper) {
           this.looper = looper;
       }

       @Override
       public boolean isMainThread() {
           return looper == Looper.myLooper();
       }

       @Override
       public Poster createPoster(EventBus eventBus) {
           return new HandlerPoster(eventBus, looper, 10);
       }
   }

}

而这个handler内部有一个enqueue方法

public void enqueue(Subscription subscription, Object event) {
	//PendingPost可以理解为 是对一个订阅者信息及发送事件封装的 消息体,这个消息体需要放到消息队列中
	//PendingPost对象是从PendingPost池中取值
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    //同步处理
    synchronized (this) {
    	//queue是PendingPostQueue类型的,PendingPostQueue可以理解为一个队列,存放PendingPost消息体
    	//消息入队列操作
        queue.enqueue(pendingPost);
        //判断handler是否启动
        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)方法,此方法内部将pendingPost中的内容解析出来,接着调用invokeSubscriber(Subscription subscription, Object event),代码不再贴出来了
            eventBus.invokeSubscriber(pendingPost);
            //下面这部分逻辑的意思是:while(true)循环不能超过10毫秒、超过后,就需要停止当前循环,并重新发送消息重新进入handlemessage方法,也就是说handlemessage方法最长只能执行10毫秒
            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;
    }
}

找到根源了,一句话mainThreadPoster.enqueue(subscription, event);这个方法就是一个自定义的handler的对象调用自己的enqueue方法。将消息体存入队列中,然后由自己来处理、最终调用到invokeSubscriber(Subscription subscription, Object event)方法中。

BACKGROUND模式:此模式中主要看backgroundPoster.enqueue(subscription, event);这句代码做了什么工作:backgroundPoster就是一个线程对象,也有一个equeue方法,将将消息入队列,并开启线程工作,然后在run方法中,从队列取出消息体。并调用eventBus的eventBus.invokeSubscriber(pendingPost);方法,代码不贴了。跟上面那个大体差不多

ASYNC模式:也是开启一个线程,入队消息,在子线程中取消息执行eventBus.invokeSubscriber(pendingPost);方法,但是跟Background模式的区别是,消息入队的时候,没有做同步处理,而且发送消息的时候,也没有做同步的检查,而且它也不会等订阅方法执行完再发送下一个消息。

EventBus性能的提升,请看EventBus源码解析(三)———EventBus注册编译期获取订阅者信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值