EventBus 流程解析

2024-08-31 01:38
文章标签 流程 解析 eventbus

本文主要是介绍EventBus 流程解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
先介绍控件使用方法,然后再从基本的使用方法断点调试,整体了解一下流程。

EventBus 基本使用

在 module 的 build.gradle添加

implementation 'org.greenrobot:eventbus:3.1.1'

在接收消息的地方注册 eventBus

EventBus.getDefault().register(this);

然后创建一个事件类

public class FirstEvent {public String message;public FirstEvent(String message){this.message = message;}
}

在注册 EventBus 的类中创建接收事件方法

// 这里的threadMode可以设置不同的值,在不同的线程中处理事件接收
@Subscribe(threadMode = ThreadMode.MAIN)
public void setText(FirstEvent event){textView.setText(event.message);
}

然后在任意一个地方发送一个消息

EventBus.getDefault().post(new FirstEvent("来自第某个地方的消息"));

EventBus一个基本的流程就这样完成了,接下来看每一步的源码

注册流程

EventBus.getDefault()

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {if (defaultInstance == null) {synchronized (EventBus.class) {if (defaultInstance == null) {defaultInstance = new EventBus();}}}return defaultInstance;
}

这是一个单例方法,用的是 DoubleCheck 的方式,创建了一个 EventBus 的实例。

register()

public void register(Object subscriber) {Class<?> subscriberClass = subscriber.getClass();List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}
}

简单来看,register 方法是将注册的类与该类中事件处理的方法进行一个绑定。

SubscriberMethod 类是一个实体类,将订阅相关的数据进行一个封装:
image.png

然后我们看一下 subscriberMethodFinder.findSubscriberMethods() 方法

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) {return subscriberMethods;}if (ignoreGeneratedIndex) {subscriberMethods = findUsingReflection(subscriberClass);} else {subscriberMethods = findUsingInfo(subscriberClass);}if (subscriberMethods.isEmpty()) {throw new EventBusException("Subscriber " + subscriberClass+ " and its super classes have no public methods with the @Subscribe annotation");} else {METHOD_CACHE.put(subscriberClass, subscriberMethods);return subscriberMethods;}
}

findSubscriberMethods() 方法会先从 METHOD_CACHE 中查找,没有找到则会根据 ignoreGeneratedIndex 标志选择是采用反射的方式寻找还是用 findUsingInfo 方法,这里这个标志和 findUsingInfo 方法目前都不知道是什么。找到后会将结果加入到缓存中。

接下来就是 subscribe() 方法

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {Class<?> eventType = subscriberMethod.eventType;Subscription newSubscription = new Subscription(subscriber, subscriberMethod);CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<>();subscriptionsByEventType.put(eventType, subscriptions);} else {if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);}}int size = subscriptions.size();for (int i = 0; i <= size; i++) {if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {subscriptions.add(i, newSubscription);break;}}List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<>();typesBySubscriber.put(subscriber, subscribedEvents);}subscribedEvents.add(eventType);// 后面是粘性事件相关处理,先不管...
}

subscribe 方法主要做两件事,一个是 subscriptionsByEventType.put(eventType, subscriptions) ,subscriptionsByEventType 是以事件的类为 key,订阅者的回调方法为 value 的映射关系表。

另一个是 typesBySubscriber.put(subscriber, subscribedEvents) , typesBySubscriber 是以订阅者为 key, 订阅者订阅的事件类为 value 的映射关系表。

到这里一个 EventBus 的注册流程就结束了,从这个流程来看,主要就是建立事件类和订阅者之间的映射关系。

注销流程

unregister()

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 {logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());}
}

注销流程就很简单了,先取出该订阅者对应的订阅事件类,然后遍历订阅的事件类,再取出该类所有的订阅者,把我们注销的订阅者移除,最后再把注销的这个订阅者从 typesBySubscriber 中移除。

Post 流程

先看下post的流程图

image.png

然后解析每个步骤,看下都做了哪些操作

post ()

public void post(Object event) {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;}}
}

PostingThreadState 的实例存储在 ThreadLocal 中,是一个内部静态类,将事件队列,post 状态,事件和订阅者等信息封装在一起。post 方法取出当前线程的PostingThreadState,将事件添加到事件队列中,设置一些状态信息,然后循环把事件队列(用一个 ArrayList 实现)的事件取出来分发。

postSingleEvent()

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();boolean subscriptionFound = false;// 标志位,决定是否要post事件所有父类和实现接口if (eventInheritance) {/* lookupAllEventTypes 方法是找到该事件的所有父类和所有实现的接口*/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) {...// 事件没有订阅者就打打日志啥的}
}

postSingleEvent 方法就两个逻辑,一个是决定是否要post事件所有父类和实现接口,另一个就是事件没有订阅者,会判断一些条件,然后重新 post 一个 NoSubscriberEvent 事件。

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

没啥多说的,根据传过来的事件,拿到对应的订阅关系,PostingThreadState 中记录信息,然后交给 postToSubscription 方法。

postToSubscription ()

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 subscriberinvokeSubscriber(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 方法。

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 是如何让事件回调在不同线程中执行的:

  • POSTTING: 不用多说,再哪个线程抛出来的事件,就再哪个线程直接反射调用回调方法,不涉及线程切换
  • MAIN: 在主线程中反射调用回调事件,如果当前线程不是主线程,则通过 mainThreadPoster 插入一个事件, mainThreadPoster 是通过主线程 Looper 创建的一个Handler,调用 enqueue() 方法,会通过自身发一个消息,然后在自己的 handleMessage() 方法中反射调用事件的回调方法,这样就完成了在主线程回调。
  • MAIN_ORDERED: 也是在主线程中反射调用回调方法,但是有顺序,不会发生第二个事件早于第一个事件执行完?不太确定。
  • BACKGROUND: 如果当前线程是主线程,则将事件添加到 backgroundPoster 中,backgroundPoster的 enqueue() 方法会通过线程池执行一个任务,反射调用回调方法。
  • ASYNC: 无论在哪个线程,都交给线程池去处理这个事件。

到这,Post 流程分析基本完成。

这篇关于EventBus 流程解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122520

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service