EventBus事件分发框架解读

2024-06-02 00:18

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

全局框架解析:Trinea的EventBus 源码解析

以下是一些具体过程的分析,最好可以先学习上面的链接内容:

下面的方法是注册最终调用的方法:

 private synchronized void register(Object subscriber, boolean sticky, int priority) {List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod, sticky, priority);}}
1.通过以下方法获取当前订阅者(就是注册的当前类)中所有的事件响应方法(这些响应方法就是该类中的onEvent(),onEventMainThread(),onEventBackgroundThread(),onEventAsync()这些方法),遍历该类中的所有方法,并将这些方法封装到一个集合中,然后以订阅者的类名为key,响应事件的方法集合为value存到一个用作缓存的hashmap中。下次遍历的时候会先通过key获得已经缓存的方法集合,存在就不会重新遍历。(具体参考源码流程)

 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
2.接着循环第一步找到的所有的事件响应方法的集合,并执行subscribe方法:

 for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod, sticky, priority);}
具体方法流程:

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {Class<?> eventType = subscriberMethod.eventType;CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<Subscription>();subscriptionsByEventType.put(eventType, subscriptions);} else {if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);}}// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)// subscriberMethod.method.setAccessible(true);int size = subscriptions.size();for (int i = 0; i <= size; i++) {if (i == size || newSubscription.priority > subscriptions.get(i).priority) {subscriptions.add(i, newSubscription);break;}}List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<Class<?>>();typesBySubscriber.put(subscriber, subscribedEvents);}subscribedEvents.add(eventType);if (sticky) {if (eventInheritance) {// Existing sticky events of all subclasses of eventType have to be considered.// Note: Iterating over all events may be inefficient with lots of sticky events,// thus data structure should be changed to allow a more efficient lookup// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();for (Map.Entry<Class<?>, Object> entry : entries) {Class<?> candidateEventType = entry.getKey();if (eventType.isAssignableFrom(candidateEventType)) {Object stickyEvent = entry.getValue();checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}} else {Object stickyEvent = stickyEvents.get(eventType);checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}}
如果通过subscriptionsByEventType.get(eventType)来获取以该事件类型为key的订阅信息集合,如果为null,就会往subscriptionsByEventType的map中添加以事件类型为key,一个存放订阅信息的空集合为value,如下:

 if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<Subscription>();subscriptionsByEventType.put(eventType, subscriptions);}
之后通过一个订阅信息的优先级高低,加入到subscriptions集合中,优先级高的放在集合的前面,比如集合中有三个信息优先级分别为4,2,1;现在进来一个优先级为3的,当i=1的时候,由于3的优先级比2高,所以在集合的第一项中加加入了优先级为3的信息,所以现在里面的顺序应该为4,3,2,1.

 int size = subscriptions.size();for (int i = 0; i <= size; i++) {if (i == size || newSubscription.priority > subscriptions.get(i).priority) {subscriptions.add(i, newSubscription);break;}}

然后通过typesBySubscriber.get(subscriber)来获取以该类(就是订阅者)为key的所有响应事件的集合,如果为null就会往typesBySubscriber的map中添加以订阅者为key,一个存放该类中所有相应事件的空集合为value,然后再把响应事件添加到事件集合中如下:

 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<Class<?>>();typesBySubscriber.put(subscriber, subscribedEvents);}subscribedEvents.add(eventType);
如果注册的不是sticky事件,就不会进行下面的判断,否则就会进入下面的判断:

if (sticky) {if (eventInheritance) {// Existing sticky events of all subclasses of eventType have to be considered.// Note: Iterating over all events may be inefficient with lots of sticky events,// thus data structure should be changed to allow a more efficient lookup// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();for (Map.Entry<Class<?>, Object> entry : entries) {Class<?> candidateEventType = entry.getKey();if (eventType.isAssignableFrom(candidateEventType)) {Object stickyEvent = entry.getValue();checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}} else {Object stickyEvent = stickyEvents.get(eventType);checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}
如果注册的是sticky事件,则会试图从stickyEvents的map中获取所有的sticky事件,通过eventType.isAssignableFrom(candidateEventType)判断注册的该事件是否已经在其他类注册过,如果注册过,就会调用下面的方法吧事件post到相应的队列中去或者直接调用(前提是之前已经通过post标记为sticky的事件到stickyEvents中):

 private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)// --> Strange corner case, which we don't take care of here.postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());}}

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {switch (subscription.subscriberMethod.threadMode) {case PostThread:invokeSubscriber(subscription, event);break;case MainThread:if (isMainThread) {invokeSubscriber(subscription, event);} else {mainThreadPoster.enqueue(subscription, event);}break;case BackgroundThread: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);}}


PostThread:在post所在的线程直接调用方法,如果在主线程调用post,就是响应事件方法也会在主线程调用,不能执行耗时操作;如果在子线程调用psot,就是响应事件方法也会在子线程调用,这时候不能直接操作主线程中与UI相关的空间,如需要操作UI需要handler或者runOnUiThread(action)。或者使用下面的方法

MainThread:在主线程处理响应事件的方法,如果post在主线程,直接调用事件响应的方法,如果post在子线程,就会先把响应事件通过 mainThreadPoster.enqueue(subscription, event),加入到在HandlerPoster中维护的一个PendingPostQueue队列中:

 void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost);if (!handlerActive) {handlerActive = true;if (!sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");}}}}

由于HandlerPoster是继承Handler,并且绑定了主线程,所以在通过sendMessage(obtainMessage())发送通知,收到通知后就会从PendingPostQueue队列中拿出事件响应方法,并调用:

   @Overridepublic 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 synchronizedpendingPost = queue.poll();if (pendingPost == null) {handlerActive = false;return;}}}eventBus.invokeSubscriber(pendingPost);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;}}
BackgroundThread:在子线程中执行事件响应的方法(只有一个子线程,就是加入到子线程中的事件存放在队列中安顺序执行)。如果post在主线程,就通过backgroundPoster.enqueue(subscription, event)加入到在BackgroundPoster中维护的一个PendingPostQueue队列中:

 public void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost);if (!executorRunning) {executorRunning = true;eventBus.getExecutorService().execute(this);}}}
由于BackgroundPoster是继承Runnable接口的线程对象,然后通过eventBus.getExecutorService().execute(this)将该线程对象加入到线程池中运行,在run方法中就是从队列PendingPostQueue中拿出一个分装了事件响应方法的PendingPost对象,接着通过eventBus.invokeSubscriber(pendingPost)调用事件响应方法,这里通过executorRunning这个标记来辨别前面的事件是否执行完,如果前一个事件还没有执行完毕,又加入了一个事件,则事件先会存放在队列中,run方法在执行完第一个事件的时候接着会从队列中取出第二个方法继续执行,所以就相当于通过eventBus.getExecutorService()创建的newCachedThreadPool线程池中只有一个工作线程在执行当前的线程对象:

 @Overridepublic void run() {try {try {while (true) {PendingPost pendingPost = queue.poll(1000);if (pendingPost == null) {synchronized (this) {// Check again, this time in synchronizedpendingPost = queue.poll();if (pendingPost == null) {executorRunning = false;return;}}}eventBus.invokeSubscriber(pendingPost);}} catch (InterruptedException e) {Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);}} finally {executorRunning = false;}}
如果post在子线程,则直接通过invokeSubscriber(subscription, event)调用事件响应方法。

Async:在子线程中执行事件响应方法()。通过Executors.newCachedThreadPool()来获得一个线程池:

 public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

然后通过 asyncPoster.enqueue(subscription, event)加入到AsyncPoster维护的一个PendingPostQueue队列中,然后每enqueue一次,就调用 eventBus.getExecutorService().execute(this)来执行将当前线程对象加入到线程池中:

 public void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);queue.enqueue(pendingPost);eventBus.getExecutorService().execute(this);}

然后通过run方法从队列中拿出事件,并调用:

 @Overridepublic void run() {PendingPost pendingPost = queue.poll();if(pendingPost == null) {throw new IllegalStateException("No pending post available");}eventBus.invokeSubscriber(pendingPost);}

注意:通过registerSticky(Object subscriber)注册前提是在某个类中已经用postSticky(Object event)将某事件提交到stickyEvents中,以至于stickyEvents中存在相同的事件类型,这样才会触发用registerSticky(Object subscriber)注册的类中相应的事件,如果没有提交过则和register(Object subscriber)效果一样。一个类只能注册一次(这里的一个类用内存地址区分)。


其实这个框架是类似于Handler框架。

(一般用于跨线程局部通讯,放在Application的Handler可以实现跨线程全局通信)Handler是初始化在哪个线程,那么消息的处理就在哪个线程,而不去管发送消息的时候在哪个线程,但是handler只适用一对一或者多对一的更新,就是最后只有一处地方可以接受并处理消息;

(全局通信,可以跨进程,线程)Broadcast可以进行全局通信,不管在哪发送,但是接受者一般在主线程执行,只要接受者接受到匹配action的广播,就会处理相应的事件,但是Broadcast可以使用一对多或者多对一的更新,需要在多处跟新数据时可以使用,同时存在可以停留消息的功能。

(跨线程全局通信)EventBus是通过注册类的方法名来确定最后事件处理在哪个线程,也同样不管消息发送在哪个线程,相当于提供了一个全局的handler通信对象,但同时具备了Broadcast消息停留的功能,以及多对一和一对多的更新的功能。

对比:相比之下三者之间的功能Broadcast>EventBus>Handler















这篇关于EventBus事件分发框架解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

Python标准库datetime模块日期和时间数据类型解读

《Python标准库datetime模块日期和时间数据类型解读》文章介绍Python中datetime模块的date、time、datetime类,用于处理日期、时间及日期时间结合体,通过属性获取时间... 目录Datetime常用类日期date类型使用时间 time 类型使用日期和时间的结合体–日期时间(

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

Linux系统之lvcreate命令使用解读

《Linux系统之lvcreate命令使用解读》lvcreate是LVM中创建逻辑卷的核心命令,支持线性、条带化、RAID、镜像、快照、瘦池和缓存池等多种类型,实现灵活存储资源管理,需注意空间分配、R... 目录lvcreate命令详解一、命令概述二、语法格式三、核心功能四、选项详解五、使用示例1. 创建逻

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

解读GC日志中的各项指标用法

《解读GC日志中的各项指标用法》:本文主要介绍GC日志中的各项指标用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基础 GC 日志格式(以 G1 为例)1. Minor GC 日志2. Full GC 日志二、关键指标解析1. GC 类型与触发原因2. 堆

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一