江南带你看EventBus解说篇

    xiaoxiao2021-03-25  139

    订阅(注册):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无非就是根据参数去查找方法,进行反射调用。

    转载请注明原文地址: https://ju.6miu.com/read-9233.html

    最新回复(0)