江南带你看EventBus解说篇

2024-06-23 04:08
文章标签 解说 江南 eventbus

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

订阅(注册):EventBus的EventBus.getDefault().register(this);就是便利当前类的所有方法,寻找以onEvent开头的放大,以键值队的形式存储。
发布:EventBus.getDefault().post(param);

发布很简单就是调用这个方法,然后EventBus就会在内部存储的方法中扫描,找到参数匹配的就会调用反射去执行,它的内部就是维持一个Map集合,键就是当前类的class类型。然后根据你传入参数的类型进行查找相应的方法,你们觉得还是个事么?
过程就是在通过register注册,Map(当前类的class类型,)

下面我们通过源码来看下EventBus的执行流程。

public static EventBus getDefault() {if(defaultInstance == null) {Class var0 = EventBus.class;synchronized(EventBus.class) {if(defaultInstance == null) {defaultInstance = new EventBus();}}}return defaultInstance;
}

通过我们java基础可以知道,这样做的好处
1:为了防止并发多线程的访问我们加了同步,但这样会影响效率,因为每次走这里都得排队等待,比较慢。
2:为了解决上面每次进来都得排队等待的过程,通过两个非空判断,提高了执行效率

接着我们看下register都做了什么?

EventBus.getDefault().register(this);
public void register(Object subscriber) {this.register(subscriber, "onEvent", false, 0);
}
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(), methodName);Iterator var7 = subscriberMethods.iterator();while(var7.hasNext()) {SubscriberMethod subscriberMethod = (SubscriberMethod)var7.next();this.subscribe(subscriber, subscriberMethod, sticky, priority);}}

ubscriber 是我们扫描类的对象,也就是我们代码中常见的this;
methodName 这个是写死的:“onEvent”,用于确定扫描什么开头的方法,可见我们的类中都是以这个开头。
sticky 这个参数,解释源码的时候解释,暂时不用管
priority 优先级,优先级越高,在调用的时候会越先调用。
通过findSubscriberMethods可以看出传入一个class类型,以及一个方法的前缀,返回的List,肯定是去遍历该类的所有方法,根据传入的方法前缀去匹配,匹配成功后返回一个封装的list。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {String key = subscriberClass.getName() + '.' + eventMethodName;Map clazz = methodCache;List subscriberMethods;synchronized(methodCache) {subscriberMethods = (List)methodCache.get(key);}if(subscriberMethods != null) {return subscriberMethods;} else {ArrayList var23 = new ArrayList();Class var24 = subscriberClass;HashSet eventTypesFound = new HashSet();for(StringBuilder methodKeyBuilder = new StringBuilder(); var24 != null; var24 = var24.getSuperclass()) {String name = var24.getName();if(name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {break;}Method[] methods = var24.getMethods();Method[] var13 = methods;int var12 = methods.length;for(int var11 = 0; var11 < var12; ++var11) {Method method = var13[var11];String methodName = method.getName();if(methodName.startsWith(eventMethodName)) {int modifiers = method.getModifiers();if((modifiers & 1) != 0 && (modifiers & 1032) == 0) {Class[] parameterTypes = method.getParameterTypes();if(parameterTypes.length == 1) {String modifierString = methodName.substring(eventMethodName.length());ThreadMode threadMode;if(modifierString.length() == 0) {threadMode = ThreadMode.PostThread;} else if(modifierString.equals("MainThread")) {threadMode = ThreadMode.MainThread;} else if(modifierString.equals("BackgroundThread")) {threadMode = ThreadMode.BackgroundThread;} else {if(!modifierString.equals("Async")) {if(!skipMethodVerificationForClasses.containsKey(var24)) {throw new EventBusException("Illegal onEvent method, check for typos: " + method);}continue;}threadMode = ThreadMode.Async;}Class eventType = parameterTypes[0];methodKeyBuilder.setLength(0);methodKeyBuilder.append(methodName);methodKeyBuilder.append('>').append(eventType.getName());String methodKey = methodKeyBuilder.toString();if(eventTypesFound.add(methodKey)) {var23.add(new SubscriberMethod(method, threadMode, eventType));}}} else if(!skipMethodVerificationForClasses.containsKey(var24)) {Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + var24 + "." + methodName);}}}}if(var23.isEmpty()) {throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + eventMethodName);} else {Map var25 = methodCache;synchronized(methodCache) {methodCache.put(key, var23);return var23;}}}
}

你只要记得一件事:扫描了所有的方法,把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList )中;
eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了执行改方法所需的一切

register完毕后,知道了EventBus如何存储我们的方法的,下面我们看看post是如何调用我们的方法的。
由于之前我们知道eventbus通过map集合把我们的方法存储到了subscriptionsByEventType中,那么是post中肯定会去subscriptionsByEventType中去取方法然后调用。

public void post(Object event) {EventBus.PostingThreadState postingState = (EventBus.PostingThreadState)this.currentPostingThreadState.get();List 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");} else {try {while(!eventQueue.isEmpty()) {this.postSingleEvent(eventQueue.remove(0), postingState);}} finally {postingState.isPosting = false;postingState.isMainThread = false;}}}
}

由第二三行代码可知道,当调研post的时候就回把当前线程的PostingThreadState存储到eventQueue中
把我们传入的event,保存到了当前线程中的一个变量PostingThreadState的eventQueue中。
10行:判断当前是否是UI线程。
16-18行:遍历队列中的所有的event,调用postSingleEvent(eventQueue.remove(0), postingState)方法。
这里大家会不会有疑问,每次post都会去调用整个队列么,那么不会造成方法多次调用么?
可以看到第7-8行,有个判断,就是防止该问题的,isPosting=true了,就不会往下走了。
这里写图片描述

下面再看下postSingleEvent()这个参数就是我们传入的实参。
然后根据这个
将我们的event,即post传入的实参;以及postingState传入到postSingleEvent中。
2-3行:根据event的Class,去得到一个List

private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error {Class eventClass = event.getClass();List eventTypes = this.findEventTypes(eventClass);boolean subscriptionFound = false;int countTypes = eventTypes.size();for(int h = 0; h < countTypes; ++h) {Class clazz = (Class)eventTypes.get(h);CopyOnWriteArrayList subscriptions;synchronized(this) {subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(clazz);}if(subscriptions != null && !subscriptions.isEmpty()) {Iterator var11 = subscriptions.iterator();while(var11.hasNext()) {Subscription subscription = (Subscription)var11.next();postingState.event = event;postingState.subscription = subscription;boolean aborted = false;try {this.postToSubscription(subscription, event, postingState.isMainThread);aborted = postingState.canceled;} finally {postingState.event = null;postingState.subscription = null;postingState.canceled = false;}if(aborted) {break;}}subscriptionFound = true;}}if(!subscriptionFound) {Log.d(TAG, "No subscribers registered for event " + eventClass);if(eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {this.post(new NoSubscriberEvent(this, event));}}}

下面看如何执行反射

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {switch($SWITCH_TABLE$de$greenrobot$event$ThreadMode()[subscription.subscriberMethod.threadMode.ordinal()]) {case 1:this.invokeSubscriber(subscription, event);break;case 2:if(isMainThread) {this.invokeSubscriber(subscription, event);} else {this.mainThreadPoster.enqueue(subscription, event);}break;case 3:if(isMainThread) {this.backgroundPoster.enqueue(subscription, event);} else {this.invokeSubscriber(subscription, event);}break;case 4:this.asyncPoster.enqueue(subscription, event);break;default:throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);}}

直接反射调用;也就是说在当前的线程直接调用该方法;
case MainThread:
首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。
case BackgroundThread:
如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用
executorService = Executors.newCachedThreadPool();。
case Async:将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。
这么说BackgroundThread和Async有什么区别呢?
BackgroundThread中的任务,一个接着一个去调用,中间使用了一个布尔型变量handlerActive进行的控制。
Async则会动态控制并发。

到此,我们完整的源码分析就结束了,总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。分析这么久,一句话就说完了~~
其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。

到此,我们完整的源码分析就结束了,总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。分析这么久,一句话就说完了~~
其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。

这篇关于江南带你看EventBus解说篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

DDoS对策是什么?详细解说DDoS攻击难以防御的理由和对策方法

攻击规模逐年增加的DDoS攻击。据相关调查介绍,2023年最大的攻击甚至达到了700Gbps。 为了抑制DDoS攻击的危害,采取适当的对策是很重要的。 特别是在网站显示花费时间或频繁出现504错误的情况下,可能已经受到了DDoS攻击,需要尽早采取对策。 本文将介绍受到DDoS攻击时的事件、受害内容和作为DDoS对策有效的三种服务。 到底什么是DDoS攻击? 理解事件、手段和损害 D

EventBus-Vue事件总线解析与使用指南

前言         在Vue.js中,组件通信是开发过程中非常场景的需求。根据不同的场景和需求,Vue提供了多种组件通信方式。比如父子组件通信、兄弟组件通信、跨代组件通信等。当应用程序中两个组件或者说页面之间没有引入和被引入的关系的时,或者说他们之间嵌套的结果复杂的时候,我们可以考虑如何传递数据呢?         其中适用的通信方式有 Vuex、provide 和 inject、Event

go程序解说

Go(通常称为 Golang)是一种开源的编程语言,由 Google 开发,于 2009 年首次发布。 Go 语言的设计目标是结合 C 语言的性能和效率以及现代编程语言的高级特性,如垃圾回收、类型安全和并发控制。 Go 语言以其简洁、高效和易于学习的特点而受到开发者的青睐,尤其适合系统编程、网络服务和并发处理。 关键特点: 简洁的语法:Go 语言的语法简洁明了,易于阅读和编写。并发支持:G

日志系统开发总结之Guava/EventBus

http://www.toutiao.com/a6351265293244301570/?tt_from=mobile_qq&utm_campaign=client_share&app=explore_article&utm_source=mobile_qq&iid=5840657922&utm_medium=toutiao_ios

python读取excel数据详细解说

在Python中读取Excel数据,最常用的库是pandas配合openpyxl或xlrd。这里,我将详细介绍使用pandas和openpyxl的方式,因为它支持更广泛的Excel文件格式,包括.xlsx和.xlsxb,且功能更强大。 安装必要的库 首先,确保你的Python环境中安装了pandas和openpyxl。可以通过pip命令来安装: pip install pandas op

EventBus搭配LifeCycle可能更美味

简要介绍 EventBus:一个用来在组件之间发通知通信的开源库。 LifeCycle:JetPack库中一个能感知生命周期的组件。 Kotlin扩展函数:可以为已经存在的类添加新的方法的黑魔法。 解决的问题 在使用EventBus时,我们每次在需要接受通知的地方,都需要注册和解绑监听函数。类似下面的模板代码: @Overrideprotected void onStart() {s

详细解说ecmascript和javascript的区别

ECMAScript 和 JavaScript 之间的关系是一个经常被误解的主题,实际上二者关系密切但又有所区别。 定义与关系: ECMAScript 是一个由 ECMA 国际(一家独立的标准组织)维护的脚本语言的标准化规范。ECMA-262 是该规范的正式名称,它定义了脚本语言的语法、类型、结构、关键字、操作符、对象和方法等。JavaScript 是 ECMAScript 规范的最广为人

EventBus 流程解析

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

C#骑砍逻辑类Mod制作详细解说

前言: 最近在研究骑砍的mod,主要是想修改其中的逻辑部分,因此有了这篇帖子。 一,文件夹与XML配置 在Modules创建一个新文件夹,文件夹名称随意,不影响实际的读取。 文件夹下面的位置需要固定,因为我只是为了修改逻辑,所以只需要bin\Win64_Shipping_Client,这是之后放dll位置的地方。 XML的配置如下: <Module><Name value

EventBus使用(一)

一、概述 EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。 1、下载EventBus的类库 源码:https://github.com/greenrobot/EventBus 2、基本使