江南带你看EventBus解说篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012070360/article/details/60955282

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

猜你喜欢

转载自blog.csdn.net/u012070360/article/details/60955282
今日推荐