EventBus源码解析(二)—发布事件和注销流程

EventBus 源码解析 使用方法 使用方法很简单,按照官方文档介绍,分为三个步骤。 步骤1:定义事件 public static class MessageEvent { } 步骤2:准备订阅者 定义订阅方法,来处理收到的事件。 @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */}; 并在Activity、Fragment中按照其生命周期进行注册与注销。 @O 阅读详情

1.EventBus源码解析(一)—订阅过程
2.EventBus源码解析(二)—发布事件和注销流程

前言

上一篇博客已经比较详细的讲解了EventBus的注册过程,有了上一篇博客的基础,其实关于EventBus的源码中的其他流程就非常好理解了,尤其是我认为EventBus中最为重要的两个Map,理解了两张图其实就理解了EventBus的原理。

源码分析

1.发布事件

post方法
/** Posts the given event to the event bus. */
    public void post(Object event) {
        //ThreadLocal保存的,不同的线程互相不干扰
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        // 将事件添加进当前线程的事件队列
        eventQueue.add(event);

        if (!postingState.isPosting) {//如果当前线程正在发送
            postingState.isMainThread = isMainThread();
            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方法。
这里首先要注意一点,这里的currentPostingThreadState是一个ThreadLocal类型,既然选择TreadLocal类型,那么特点当然就是线程单例,不同的线程之间互相不干扰,因为我们知道EventBus是支持多线程之间的事件传递的。
接下来的源码都比较简单,将事件加入当前线程中用于保存事件的队列中,然后遍历队列中的事件,执行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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

可以看到这里的流程比较简单,基本都是最后调用了postSingleEventForEventType方法,当然如果没有找到,则会发送一条没有找到的NoSubscriberEvent.
这里有个比较重要的参数eventInheritance,这个如果我们没有特殊配置的话,默认是true,我们来看一下这个参数是干什么的,可以看到当这个参数为true的时候,会调用lookupAllEventTypes方法,返回一个List集合。

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

可以看到,这个方法的所用就是用一个List保存该Event类型自己本身,和其继承的所有父类,和实现的所有接口。
所以当得到这个List集合后,会遍历这个集合,调用postSingleEventForEventType发送事件,所以这里可以看出默认情况下不要随意将Event继承或者实现接口,当发送该Event的时候,都会一并发出。

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

这时就可以看到我们前一篇博客提到的非常重要的一个Map:subscriptionsByEventType,这里放上这个Map的图便于理解。
subscriptionsByEventType
所以我们通过Event的类型,找到了所有订阅该Event的信息CopyOnWriteArrayList<Subscription>。接下来就是遍历这个List调用postToSubscription方法,利用反射来调用订阅的方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            //默认类型
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    //如果当前就在UI线程,则直接反射执行
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                //不同于MAIN,直接通过Handler的队列执行,串行的
                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) {
                    //如果当前是UI线程,则异步
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    //不是UI线程,则在该线程执行
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

可以看到这里实现了EventBus中的多线程功能,具体实现本篇博客就不做讲解了,后面如果有时间,会讲解一下EventBus中的多线程。
可以看到最后其实都是调用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);
        }
    }

果然不出所料,最后就是通过我们经常看到的反射里调用订阅者中的订阅方法,来实现EventBus中的事件流程。

2.注销流程

unregister
public synchronized void unregister(Object subscriber) {
        //找到订阅者订阅的所有事件类型,也就是MainActivity中所有的Event
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                //到Event的map中删除MainActivity
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

可以看到这里又用到我们所说的重要的Map中的其中一个typesBySubscriber,这里再放一下这个Map的图便与我们理解。
typesBySubscriber

可以看到这里,从typesBySubscribermap中取出当前订阅者的订阅的所有事件集合List,然后再遍历这个List,调用unsubscribeByEventType方法进行取消订阅,其实很容易联想到这个方法肯定是再到另一个重要的Map中查找订阅了这个Event的订阅者中移除当前Activity的订阅者。

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                //判断两个变量指向的是否是同一个内存地址
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

果然不出所料,又看到我们熟悉的另一个MapsubscriptionsByEventType,这里遍历该Activity中订阅的所有事件,然后通过Event到subscriptionsByEventType中查找订阅了该Event的List<Subscription>集合,然后找到当前这个订阅者(注意这里使用的是==,来判断两个Object对象是否相等,意味着是内存地址相同),找到后移除。至此注销流程也到此结束。

3.总结

通过本篇博客,EventBus的基本流程基本上我们已经有了一定程度上的理解,详细阅读了这两篇博客后对于第一篇博客中提出的问题也能相应理解。而且更重要的贯彻EventBus整个流程的两个重要的Map。

深入理解EventBus:多版本对比与源码解析 EventBus 是一个发布/订阅事件总线,专为Android设计,但同样适用于Java。它简化了组件间的通信,允许发布和订阅事件,而无需持有对方的引用。这一点尤其适合解耦应用程序中的各个模块。Publisher,顾名思义,是指那些发布事件的组件。在EventBus框架中,任何对象都可以成为一个事件发布者。发布事件的基本方法是通过post方法。发布事件是异步执行的,即发布操作会在一个后台线程中执行,从而不会阻塞当前线程,提升性能并改善用户体验。EventBus的订阅者优先级是通过。 阅读详情

相关推荐

C# EventBus事件总线实现与源码解析

在C#编程中,事件是一种特殊的多播委托,用于实现发布/订阅模式。事件允许对象或类提供通知,而其他对象可以订阅这些通知。事件常用于用户界面和设计模式中,例如在图形用户界面(GUI)中,按钮点击或窗口关闭等行为通常通过事件来处理。事件的定义使用了event关键字,并且需要声明一个委托类型来指定事件的签名,即事件处理函数的参数和返回类型。以下是一个简单的事件定义和使用示例:// 定义一个事件,委托类型为MyEventHandler// 方法来触发事件。

weixin_35728286的博客 582

vue使用eventBus时,关闭页面销毁该方法

由于eventBus的$on方法在创建后即使关闭该页面也不会自动销毁,所以会出现在a页面创建之后,b页面点击也触发a页面的方法。在两个页面使用同一个EventBus,会同时触发});二、解决思路在关闭页面或者切换路由触发beforeDestroy时,销毁掉该$on方法以免被其他页面误触},

q1740706590的博客 796

手把手教你理解瑞萨RH850芯片的HSM:从硬件隔离到软件中断通信

本文深入解析瑞萨RH850芯片的HSM架构,从硬件隔离到软件中断通信的完整实现。通过详细讲解HSM的硬件组件、启动流程、核间通信机制及安全服务实现,帮助开发者掌握芯片级安全技术,提升汽车电子和工业控制领域的安全防护能力。

weixin_33925615的博客 379

EventBus 使用问题总结

1.No subscribers registered for event class EventBus不适合向一个不存在于activity栈中的activity发送消息,这样是失败的. 情况1:一个activity 还没有生成,就post,肯定报这样的错; 情况2:一个activity曾经生成了,但是不在activity栈中了,也是收不到消息的 情况3:生命周期的问题 官方推荐是这样...

qq_29443203的博客 1599

基于FPGA的函数信号发生器设计

设计采用波形查找表和相位累加器的方法实现DDS,查找表的数据位宽为8位,采样点数为4096。波形产生范围是100Hz-20MHz,最小频率间隔为1Hz,用8个数码管显示频率,可产生正弦波、方波、三角波,波形用两个led显示,select信号控制,全灭为正弦波,一亮一灭为方波,全亮为锯齿波。为了使频率和相位便于调节,用3个拨码开关控制步进,用8个led显示状态。

FPGA入门到跑路 5636

CloudCompare点云变换保姆级教程:从平移、旋转到绕任意点旋转,一次搞定

本文提供CloudCompare点云变换的全面教程,涵盖从基础平移、旋转到绕任意点旋转的高级技巧。通过详细的操作步骤和实战案例,帮助用户掌握点云数据处理的核心技能,提升三维模型对齐和变换的效率与精度。

weixin_28717969的博客 291

eventbus注销的事件取消不掉

派发事件 注意: this.EventBus.off(′event′);不能传参数,this.EventBus.off('event');不能传参数,this.EventBus.off(′event′);不能传参数,this.EventBus.off(‘event’, {}); 是不行的大家谨慎使用EventBus,如果事件车变多会导致方法名不够用哈,另外一定要在绑定的页面进行off卸载...

weixin_45631278的博客 725

nmap、X-Scan漏洞扫描和Metasploit中用永恒之蓝漏洞入侵靶机并上传冰河

利用nmap、X-Scan扫描靶机的漏洞,在Metasploit中用扫描出的永恒之蓝漏洞入侵靶机,建立连接后上传冰河木马控制靶机,最后用冰河陷阱清除靶机的冰河木马,记一次完整的入侵控制实验。

m0_59302403的博客 4407

EventBus(一):注册、分发、注销

1 EventBus简介EventBus是一个用于简化Andorid、Fragment、Threads、Service之间信息传递的一个发布/订阅事件集。 使用EventBus的建议: 并不建议将应用中所有的事件都通过EventBus进行发送,尤其对于一对一的组件之间通信,建议不要使用EventBus。EventBus的使用场景更像是一种广播,当我们向EventBus发送一个事件,则该事件将会传

SuperBin的博客 2万+

ASPP - 空洞空间金字塔池化

文章目录1 空洞卷积1.1 空洞卷积的理解1.1.1 一维1.1.2 二维1.2 空洞卷积的优劣2. ASPP3. 代码 1 空洞卷积 1.1 空洞卷积的理解 1.1.1 一维 (a) 正常卷积:输入特征 Input feature,kernel = 3,stride = 1,pad = 1,输出特征 Output feature。 (b) 空洞卷积:与图 (a) 不同之处在于 pad = 2,同时引入一个 rate = 2(表示卷积核中参数间间隔的超参)。   还可通过下图进一步理解普通卷积与空洞卷

晓野豬 2万+

UGUI合批杀手:Mask/RectMask2D为何必断合批?

颜色缓冲负责记录像素颜色;模板缓冲负责记录“这个像素的模板值”(一个小整数);渲染时,你可以设定:只有模板值为 X 的地方才允许画;或者往某些区域写特定的模板值。类似于:先拿一支隐形笔在纸上画出一块区域,给它记一个编号“1”,后面的涂色动作只允许涂在“编号为1”的区域上。合批的前提:渲染状态不变,连续元素能一口气画完。材质、贴图、混合模式、Stencil、裁剪区域……都得一致。Mask / RectMask2D 的本质:在渲染状态上加规则——“只在某个区域内允许写像素”。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 119

Vue2 封装全局事件总线(Event Bus) 实现跨组件通信 - 附完整示例

Vue2 封装全局事件总线(Event Bus) 实现跨组件通信 - 附完整示例

m0_48968874的博客 1676

归结法证明真题分析

本文主要探讨了逻辑证明题的三种核心形式及其解题策略。第一类为A1∧A2∧A3→B的证明,常采用线性、单元、支持策略法等归结法,典型案例包括使用归纳法证明特定逻辑公式,并列举了历年真题。第二类为集合交集证明,典型案例为证明任意集合S和T的交集S∩T是S的子集,需将交集和包含关系用逻辑式表达。第三类为常规归结法证明,真题示例为使用归结法证明特定逻辑蕴含关系。掌握前两类证明方法后,第三类问题自然迎刃而解。

super_mochi1 278

Android之EventBus源码解析

这里写目录标题一级目录二级目录三级目录 一级目录 二级目录 三级目录

lizhichao628的专栏 1533

Open3D (C++) 点云边界提取【2025最新版】

Compute the boundary points of a point cloud.The implementation is inspired by the PCL implementation. Reference:https://pointclouds.org/documentation/classpcl_1_1_boundary_estimation.html

点云侠的博客 871

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

深度探秘!Android EventBus 事件注册与注销管理模块全解析(12)

EventBus 是基于发布 - 订阅模式的事件总线库,其核心思想是将事件的发布者和订阅者进行解耦。在传统的 Android 开发中,组件之间的通信往往需要复杂的接口和回调机制,这不仅增加了代码的复杂度,还降低了代码的可维护性。而 EventBus 通过引入事件的概念,让发布者只需将事件发布到 EventBus 上,订阅者则可以订阅自己感兴趣的事件,当相应的事件被发布时,EventBus 会自动将事件分发给订阅者。这种模式使得组件之间的依赖关系大大降低,提高了代码的可维护性和可扩展性。

Android开发领域创作者 613

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

EventBus源码解析

今天开始拆分EventBus源码,废话少说,五步骤,如何使用,源码解析,学到了什么,有什么不足之处 第一部分:如何使用 //第二步:注册 接收类 EventBus.getDefault().register(this); //第三步,发送事件 发送事件 事件参数 EventBus.getDefault().post(new Object()); //第四步,注销注册 EventBus.g

zhq217217的博客 2778

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android开发知识(十四):《EventBus3.1.1源码解析(上篇)》

EventBus前言 本文主要讲解EventBus的源码解析,如果您未听过/使用过EventBus的话请自行百度一下,几分钟即可GET到这门技能。EventBus的好处是实现类与类之间通讯的解耦:内部采用观察者模式(发布-订阅模式),该模式可以把发布者和订阅者解耦出来,两者并不需要做直接交互。 然而有利有弊,EventBus使用不当则容易造成代码难以解读,很多时候你并不知道观察者会在什么时候接收消...

lc_miao的专栏 1万+

EventBus 流程解析

先介绍控件使用方法,然后再从基本的使用方法断点调试,整体了解一下流程。 ## EventBus 基本使用 在 module 的 build.gradle添加 implementation 'org.greenrobot:eventbus:3.1.1' 在接收消息的地方注册 eventBus EventBus.getDefault().register(this); 然后创建一个事件类 pub...

DoubleCake的专栏 602

一篇文章搞定《EventBus源码详解(全)》

最近几篇文章会对常见的三方库有一个清晰的解析。包含使用场景、基本使用、源码解析、常见问题等等几个方面。希望在源码解析的过程能学习到,三方库设计的理念和思想。本篇文章也是请到了EventBus来做客会以EventBus的v3.3.1版本对此进行解析。补:做好准备,看源码就是很崩溃的(都快给我写崩溃了)。但是源码的重要性就不用多BB了。总结就是,你看到总结了。说明你注定会变强!!!!!!!!

小孟来码的博客 4789

Android EventBus源码解析

一、EventBus的简单使用流程 《Android EventBus的简单使用》中介绍了EventBus的简单使用,从 EventBus.getDefault().register(this); 开始,注意在类中实现相应的onEvent方法:   public void onEventMainThread(ActivityToServiceEvent even

woliuyunyicai的专栏 934

EventBus源码解析一

IntentHandlerInterfaceAIDLMessenger1.3、思考。

2401_87034019的博客 1538

EventBus使用及其源码解析

EventBus使用及其原理解析什么是EventBus?EventBus的使用创建一个发送事件类定义订阅者方法注册和注销订阅事件发送消息EventBus的源码导读Subscribe注解的使用两个全局变量map注册流程EventBus.getDefault()register(this)findSubscriberMethods()findUsingInfo()方法subscribe()方法注销流程unregister()unsubscribeByEventType(subscriber, eventType

LY010106的博客 472

EventBus3 源码解析(个人理解) 之一

前言: 还记得我曾经在知乎上面提问:说怎么看源码,什么样的姿势比较好? 但是也很少人回答我。但是也没有关系。原来我不知道怎么回答,现在 估计能回答一点点了。姿势_1: 就拿EventBus框架来说吧!我们首先应该会用是吧. 基本功能: 注册: EventBus.getDefault().

记录自己 5568

EventBus源码解析,逐步源码流程

说明 EventBus是Android常用的三方框架之一。通过发送通知,在其他页面去做一些同步的事情,方便快捷。类似于观察者(但并不是),当观察到某个数据变化,就会立刻做改变,但是和内部和观察者其实不一样,它的思想是AOP切面编程思想。内部代码不多,大概30个类。 源码分析 1) getDefault() /** Convenience singleton for apps using a process-wide EventBus instance. */ public static Event

Sunanang的博客 264

【Android】EventBus 3.0 源码分析

概述EventBus是一个基于观察者模式的事件发布/订阅框架,开发者可以通过极少的代码去实现多个模块之间的通信,既可用于 Android 四大组件间通讯,也可以用于异步线程和主线程间通讯,而不需要以Interface回调、handler或者BroadCastReceiver的形式去单独构建通信桥梁。从而降低因多重回调导致的模块间强耦合,同时避免产生大量内部类。这是EventBus源码中的介绍:/**

Mlib 1753
上一篇: EventBus源码解析(一)—订阅过程
下一篇: RichEditor——一款基于RecyclerView实现的富文本编辑器实现方案(支持图文、转义生成MarkDown、粗体、斜体、下划线、删除线、超链接、标题等)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值