EventBus的使用和源码解析

闲在家里?看看EventBus解析,跟我一起一起手写EventBus 前言 本来通知这周一复工的,结果好像事情并没有简单,又推迟到下周1了,还要在家办公一段时间,昨天有粉丝问我怎么没有更文,昨天分批次去公司开了一下午的会议,索性大家人数分批开会都带了口罩,所以一点都不慌,大家出门都记得戴口罩,勤洗手~(在家办公还是蛮快乐的,可以边唱歌边办公_) 前几天更新了OKHTTP和Retrofit的解析,今天更最后一篇EventBus的解析。 把最近的知识也整理汇总了一份,打... 阅读详情

一、基本介绍

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。EventBus

EventBus3.0版本有较大的更新,性能上有很大提升。这里主要介绍新版本。

传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。类似框架OTTO。

二、EventBus & Otto对比

共同点

1、都是事件总线框架,满足消息/事件传递的同时,也实现了组件间的解耦.
2、注册的共同点都是采用method方法进行一个集成。

3、都采用注解的方式来标注订阅方法(旧版本的EventBus通过固定方法名标记订阅者)

4、大部分规则相同,比如订阅方法只能有一个参数。

5、都不适用进程间通信

不同点

1、OTTO更加轻量级,结构简单。EventBus稍微复杂一些。

2、OTTO默认在主线程中使用,不能在其他线程使用,通过设置ThreadEnforcer可以在任意线程使用,但是消息传递不能指定目标线程,EventBus实现了4种ThreadMode,线程之间消息传递非常灵活。
3、EventBus支持粘性事件,而OTTO不支持。即先发消息,再注册订阅者仍然能够收到消息。

3、OTTO有默认的生产者方法,可以产生默认消息,EventBus没有

三、EventBus的简单使用介绍

定义消息

public class MessageEvent {
 //定义相关属性
 }

注册

eventBus.register(this);

定义订阅者

@Subscribe
public void onEvent(MessageEvent event) {
//收到消息后的处理
};

发送消息

eventBus.post(event);

解绑

eventBus.unregister(this);

四、源码解析

1、EventBus构造

通常我们调用EventBus.getDefault()获取EventBus
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

这个方法是线程安全的,EventBus的无参构造方法使用默认构建器DEFAULT_BUILDER构造,EventBusBuilder指定了EventBus的一些行为,用于输出log,查错,调试等。
logSubscriberExceptions 指定了收到SubscriberExceptionEvent类型消息时抛出异常的行为,默认为true,打印log,如果为false不输出log
输出的信息有
Log.e(TAG, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
    + " threw an exception", cause);
    SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
    Log.e(TAG, "Initial event " + exEvent.causingEvent + " caused exception in "
     + exEvent.causingSubscriber, exEvent.throwable);
Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "
      + subscription.subscriber.getClass(), cause);

对logNoSubscriberMessages指定了发送了没有订阅者的消息的行为,默认为true,打印log,如果为false不输出log
输出的信息为
Log.d(TAG, "No subscribers registered for event " + eventClass);
sendSubscriberExceptionEvent指定是否发送SubscriberExceptionEvent,默认为true
操作行为
SubscriberExceptionEvent exEvent = 
new SubscriberExceptionEvent(this, cause, event,subscription.subscriber);
post(exEvent);

sendNoSubscriberEvent指定了没有订阅者时的行为,默认为true,发送NoSubscriberEvent这样一个消息
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
              eventClass != SubscriberExceptionEvent.class) {
        post(new NoSubscriberEvent(this, event));
}

throwSubscriberException指定了订阅方法执行异常时是否抛出异常,默认为false
if (logSubscriberExceptions) {
          Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "
                  + subscription.subscriber.getClass(), cause);
}

eventInheritance指定是否发消息给这个事件的父事件对应的订阅者,默认为true。如果为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);
}
ignoreGeneratedIndex指定是否忽略设置索引,这个值默认是false,这段代码中使用的两种解析方式不容易看懂
if (ignoreGeneratedIndex) {
    subscriberMethods = findUsingReflection(subscriberClass);
 } else {
    subscriberMethods = findUsingInfo(subscriberClass);
}
findUsingReflection利用反射来获取订阅类中的订阅方法信息
findUsingInfo从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
findUsingInfo执行效率更高一些,默认使用这种。
strictMethodVerification指定是否严格校验,默认为false。严格校验会直接抛出异常
else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException("@Subscribe method " + methodName +
                    "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }

2、注册与解析

注册之后,解析出了所有的订阅方法,方法参数类型
public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

相关映射主要是两个
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
subscriptionsByEventType的key是订阅者消息类型,value是Subscription,是具体订阅者对应注册对象和方法
typesBySubscriber的key是注册对象,value是对应所有订阅者消息类型的list
解析器是SubscriberMethodFinder这个类,EventBus中有subscriberMethodFinder这个实例,调用
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)这个方法解析方法信息
如果没有订阅者方法,会抛出异常
throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
如果已经注册过,会抛出异常
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
如果订阅者是粘性事件,如果粘性事件不为空会直接发送粘性事件。
粘性事件存储结构为
private final Map<Class<?>, Object> stickyEvents;

stickyEvents的key是事件类型,Object是对应的具体事件。key,value唯一,说明粘性事件是会被覆盖的。这样可以节省内存开销。

3、发送消息

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

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线程状态通过ThreadLocal维护,保证线程隔离,不需要加锁,提高运行效率。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};


经过以下四个方法,事件分发到对应线程的对应订阅者
private void postSingleEvent(Object event, PostingThreadState postingState) 
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread)
void invokeSubscriber(Subscription subscription, Object event) 
特别注意线程处理
switch (subscription.subscriberMethod.threadMode) {
           case POSTING:
               invokeSubscriber(subscription, event);
               break;
           case MAIN:
               if (isMainThread) {
                   invokeSubscriber(subscription, event);
               } else {
                   mainThreadPoster.enqueue(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);
       }

POSTING:是在当前线程执行
MAIN:抛到主线程
BACKGROUND:如果是在主线程,抛到新线程,如果不是在主线程,就在当前线程执行
ASYNC:抛到新线程中
没有订阅者的消息默认会被重新包装为NoSubscriberEvent,可以做一些全局捕获处理

4、解绑

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
做一些清理工作,注意不要重复解绑。


刚开始已经说过了,EventBus跟OTTO很像,而 Otto 是基于 Guava 的增强的事件总线,Guava 是google一个很强大的java开源库。EventBus改进是最多的,据说是效率最高的,这个没有做过相关测试。但是从实现上来看,对性能优化处理还是做了很多事情。


欢迎扫描二维码,关注公众账号








 








Android EventBus源码解析 带你深入理解EventBus 光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想。 阅读详情

相关推荐

深入理解EventBus:多版本对比与源码解析

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

weixin_30336531的博客 880

EventBus的特性及简单的使用介绍

EventBus是一个用于Android和Java的开源库,使用发布者/订阅者模式(publisher/subscriber pattern)实现松散(loose)耦合(coupling)。EventBus使中央通信仅用几行代码就能解耦类(decouple classes)——简化代码(simplifying the code)、消除依赖(removing dependencies)和加速应用程序...

WindfallSheng的博客 905

国民技术N32G45X低功耗问题之更改全局变量和栈在RAM中的地址

最近项目中用的MCU是国民技术的N32G45X,MCU的资源很丰富,RAM也很大,配套的库和例程也比较全,用起来还是不错的,但是在调试低功耗的时候还是费了不少劲。 首先由于项目低功耗需要,只能选择停机模式2(STOP2)。根据数据手册的描述,在此模式下外部低速时钟开启, RTC运行, R-SRAM保持,所有I/O状态保持,独立看门狗处于关闭状态,25℃的条件下电流为10个微安。 用的这款MCU的RAM是144KB,分为SRAM(128KB)和R-SRAM( Retention SRAM – 16KB),如果

狂想家 2485

vue3中的event bus

event Bus在vue3中事实上已经被删除了,因为官方说它可能会导致一些潜在的问题。如,在大型项目中使用 Event Bus 可能会变得难以维护和调试,同时也可能会影响应用程序的性能。此外,在 Vue 3.x 中已经有更多先进和强大的工具来进行组件之间通信,例如 provide/inject 和 Emit/Listeners 等机制。因此,Vue 3.x 团队决定删除 Event Bus,以鼓励开发者使用更好的解决方案。但是还有很多项目是相对较小的,依然是可以使用的。

杜永康的博客 1万+

Rockchip平台之RK3566/3568 OS02N10

文章摘要 本文主要介绍了200W摄像头传感器OS02N10在RK3566平台上的兼容与适配过程。作者基于之前的400W摄像头兼容经验,针对200W摄像头进行了类似的适配工作。文章详细阐述了RK3566芯片的特性和OS02N10传感器的技术参数,包括分辨率、接口和功耗等。适配过程分为三个关键部分:DTS设备树文件配置(定义I2C地址、电源域和GPIO控制等)、驱动文件开发(寄存器初始化和操作)以及XML和JSON配置文件调整(图像参数设置)。作者特别强调了初始化数组中空寄存器终止条件的重要性,并分享了兼容过程

different_xzh的博客 2412

各种AI的调试日记——辅助工作篇

记录使用各种AI(语言大模型)的心得和总结

m0_73056986的博客 1389

reactHook实现EventBus

react,EventBus,ReactHook中使用EventBus,同步调用和异步调用。

GodLoveMe的博客 3252

5分钟搞定Unity+Visual Studio开发环境配置(含C#游戏开发工作负载)

本文提供了一份详细的Unity与Visual Studio开发环境配置指南,帮助开发者在5分钟内完成C#游戏开发工作负载的设置。从Unity Hub安装到Visual Studio关键组件选择,再到常见问题排查,全面覆盖环境搭建的各个环节,显著提升游戏开发效率。

weixin_29014237的博客 248

Use EventBus

为什么80%的码农都做不了架构师?>>> ...

weixin_33674976的博客 343

CH582低功耗实战:从1.2mA降到5uA,我的蓝牙广播功耗优化笔记

本文详细记录了沁恒CH582蓝牙芯片从1.2mA降至5μA的功耗优化全过程。通过建立基准测试环境、分层排查软件配置与IO状态、精细调优射频参数以及硬件层面的电源管理,实现了极致的低功耗表现。文章还提供了实用的功耗测量方法和典型问题排查指南,为蓝牙设备开发者提供了宝贵的参考经验。

weixin_30368405的博客 572

STM32实战开发(128):智能位移检测 - 使用STM32实现位移检测

STM32作为一款功能强大的微控制器,由于其出色的处理能力、丰富的外设接口和低功耗特点,成为实现智能位移检测系统的理想选择。未来,可以通过结合更多传感器、多种数据处理算法以及云平台技术,进一步提升系统的智能化水平,优化位移测量的精度和稳定性。本篇博客将介绍如何使用STM32微控制器实现智能位移检测系统,涉及硬件设计、传感器选择、软件开发等多个方面,提供详细的设计步骤,并给出完整的代码示例,帮助开发者快速搭建智能位移检测系统。蜂鸣器连接到STM32的GPIO端口,当位移超过设定的阈值时,蜂鸣器发出警报。

2201_76125393的博客 900

EventBus的使用(组件间通信)

主要功能:替代Intent,Handler,BroadCast 在 Activity与Activity、Activity与Fragment,Service,线程之间传递消息或数据。EventBus 3.0版本后,开发者能够自定义订阅方法的名字,而没必要规定以“onEventXX”开头的方法了,这样也自由化了很多,而且支持了粘性事件的分发等。官方混淆配置: http://greenrobot.org/eventbus/documentation/proguard/粘性事件(未完待续)

villen_t的博客 1924

【TCAD】Sentaurus 中的“陷阱trap”仿真设置

允许使用有限的数据集名称 SFactor 规范,例如 DeepLevels , xMoleFraction , 和 yMoleFraction (从兴奋剂档案中读取) eTrappedCharge 和 hTrappedCharge (从指定的文件中读取 DevFields 在 File 部分)或 PMI 用户字段 PMIUserField0...10 (从指定的文件中读取 PMIUserFields 在 File 部分)。给定的陷阱截面 eNeutral 陷阱是多种多样的 Xsec 项目参数。

荷塘阅色 1307

EventBus(事件总线)的使用和源码的简单解析

事件总线(EventBus)是一种广泛用于软件架构中的设计模式,用于实现解耦和松散耦合的通信机制。它可以帮助组织和管理应用程序中不同组件之间的通信,以提高应用程序的可维护性、可扩展性和灵活性。 在事件总线模式中,不同的组件通过订阅和发布事件来进行通信。发布者发布一个事件,订阅者可以订阅该事件并在事件发生时执行相关的操作。事件总线充当了中介者的角色,它负责管理所有的事件和订阅者,并确保事件正确地传递到所有订阅者。 事件总线模式可以在很多场景中使用,例如 Android 应用程序中的通信、分布式系统中的消息

Tai_Monster的博客 7187

Yolov8-pose关键点检测:通用倒瓶颈(UIB)搜索块结合C2f二次创新 | 轻量化之王MobileNetV4

通用倒瓶颈(UIB)搜索块结合C2f二次创新

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 613

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

EventBus简单使用和源码解析

EventBus源码分析文档 此讲解基于EventBus3.0源码Author: laoXu EventBus 目录结构如下: EventBus最基本的使用方法及步骤 1、首先先注册一个EventBus,注册方法如下: // 注册EventBus EventBus.getDefault().register(this); 2、在销毁的时

Ghoststianlangxin的专栏 615

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Google Guava:EventBus源码解析和发布订阅消息的简单使用示例

EventBus是Guava中对于事件发布订阅的功能实现,是设计模式中的发布/订阅模式的实现方案。

逆水行舟 710

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

EventBus源码解析

前言:在现在的android项目开发小伙伴们越来越多的在使用EventBus了作为事件的同步了,EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信,以其简单易懂、优雅、开销小等优点而备受欢迎。所以在之前已经整理了EventBus与Otto使用和区别之后,我最近再次梳理了EventBus的源码原理。 友情提示:该篇文章主要是介绍梳理EventBus的源码...

蒙的博客 422

EventBus简单使用及源码解析

此篇是我在公司写的一篇wiki,在此搬运过来~

weixin_44408818的博客 539

Android之EventBus源码解析

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

lizhichao628的专栏 1533

读源码-EventBus源码解析

目录1-注册1.1-核心1.2-注册具体源码2-取消注册3-事件发送4-粘性事件 1-注册 1.1-核心 注册主要原理就是EventBus的三个map变量的注册: subscriptionsByEventType | EventType为key,Subscription列表为Value typesBySubscriber | 注册对象subscriber为key,EventType列表为Value stickyEvents | EventType为key,Event对象为Value 字段说明:

SuperMing学习之路 362

eventBus源码解析

两种使用方法 不带processor的使用 //注册 EventBus.getDefault().register(this); //发送 EventBus.getDefault().post(""); //处理 @Subscribe(threadMode = ThreadMode.MAIN) public void onReceve(String s) { 带processor的使用 https://greenrobot.org/eventbus/

u013008419的专栏 174

探索Android开源框架 - 5. EventBus使用及源码解析

相关概念 定义 Android事件发布/订阅框架,简化应用程序内组件间,组件与后台线程间的通信; 优点 代码简洁,使用简单,将事件发布和订阅充分解耦; 五种ThreadMode POSTING(默认) 接收事件方法应执行在发射事件方法所在的线程 MAIN 接收事件方法应执行在主线程 MAIN_ORDERED 接收事件方法会被扔进 MessageQueue 中等待执行 BACKGROUND 发射事件方法在主线程中执行,则接收事件方法应执行在 EventBus 维护的单一子线程 执行 发射

o118abcdef的博客 925
上一篇: TCP/UDP简单介绍及JavaSocket的使用
下一篇: Android依赖注入Dagger的使用和源码解析(上篇)
robert_chao
博客等级 码龄15年 265粉丝 119原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值