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

相关文章

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

MySQL中的锁和MVCC机制解读

《MySQL中的锁和MVCC机制解读》MySQL事务、锁和MVCC机制是确保数据库操作原子性、一致性和隔离性的关键,事务必须遵循ACID原则,锁的类型包括表级锁、行级锁和意向锁,MVCC通过非锁定读和... 目录mysql的锁和MVCC机制事务的概念与ACID特性锁的类型及其工作机制锁的粒度与性能影响多版本

Redis过期键删除策略解读

《Redis过期键删除策略解读》Redis通过惰性删除策略和定期删除策略来管理过期键,惰性删除策略在键被访问时检查是否过期并删除,节省CPU开销但可能导致过期键滞留,定期删除策略定期扫描并删除过期键,... 目录1.Redis使用两种不同的策略来删除过期键,分别是惰性删除策略和定期删除策略1.1惰性删除策略

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论

禁止平板,iPad长按弹出默认菜单事件

通过监控按下抬起时间差来禁止弹出事件,把以下代码写在要禁止的页面的页面加载事件里面即可     var date;document.addEventListener('touchstart', event => {date = new Date().getTime();});document.addEventListener('touchend', event => {if (new

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL