本文主要是介绍江南带你看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解说篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!