EventBus的源码解析(一)

时间:2022-07-17 00:18:56

EventBus已经出来很久了,相信大家并不陌生,很多android程序员应该在项目中都使用过它了,当然了我也使用过,以前使用的时候就是觉得它使用起来挺简单的,但是它的具体实现原理却没有研究过,作为一名有追求的程序员,仅仅会使用是无法满足我的,今天我们就从源码来看看他是如何实现的。
EventBus的作用就是方便应用的各个部分之间的通信,且能降低彼此之间的耦合。
使用EventBus第一步就是要获取它的实例,看一下它提供的方法:

EventBus.getDefault()

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

        return defaultInstance;
    }

可以看到他使用的是双重判断的单列模式,并且考虑了并发问题。
使用EventBus主要分为两步:

1. 注册 Register(Object subscriber)

public void register(Object subscriber) {
        this.register(subscriber, false, 0);
    }
private synchronized void register(Object subscriber, boolean sticky, int priority) {
        List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        Iterator i$ = subscriberMethods.iterator();

        while(i$.hasNext()) {
            SubscriberMethod subscriberMethod = (SubscriberMethod)i$.next();
            this.subscribe(subscriber, subscriberMethod, sticky, priority);
        }

    }

首先看方法的几个参数,目前看得懂的就是Object subscriber,因为这是我们register(param)传进去。
让我们来看看List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
参数subscriber.getClass()返回的是subscriber这个对象所属的类对象(java.lang.Class),这个对象是在类加载过程中生成的,它主要是作为方法区中这个类的各个数据的访问入口(小说一句,该对象存在于HotSpot虚拟机中的方法区里面)。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        String key = subscriberClass.getName();
        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.getDeclaredMethods();
                Method[] arr$ = methods;
                int len$ = methods.length;

                for(int i$ = 0; i$ < len$; ++i$) {
                    Method method = arr$[i$];
                    String methodName = method.getName();
                    if(methodName.startsWith("onEvent")) {
                        int modifiers = method.getModifiers();
                        if((modifiers & 1) != 0 && (modifiers & 5192) == 0) {
                            Class[] parameterTypes = method.getParameterTypes();
                            if(parameterTypes.length == 1) {
                                String modifierString = methodName.substring("onEvent".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(!this.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(!this.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 " + "onEvent");
            } else {
                Map var25 = methodCache;
                synchronized(methodCache) {
                    methodCache.put(key, var23);
                    return var23;
                }
            }
        }
    }

2-10行:通过我们传入对象的类名作为键在一个map集合中查找对应的值,如果查找到了就直接返回。
22-23行:通过我们之前获得的java.lang.Class类对象利用反射去获取该类里面所有的方法。
26-66行:遍历该类中所有的方法,如果是以“onEvent”开头,并且该方法的修饰符是public且非static和abstract方法,并且该方法只有一个参数,就可以进入到封装的代码中。通过截取该方法名onEvent后面的字符串与“MainThread”,“BackgroundThread”,“Async”进行对比来确定threadMode,它是一个枚举,eventType是该方法的参数Class对象,将该方法的method,threadMode,eventType作为参数封装到一个SubscriberMethod对象当中,并将该对象添加到一个ArrayList集合当中并返回,在返回之前会吧这个以传入的类名作为键,这个ArrayList集合作为值缓存到methodCache这个map集合中,下次就可以直接从缓存中拿到ArrayList对象了。

  List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        Iterator i$ = subscriberMethods.iterator();

        while(i$.hasNext()) {
            SubscriberMethod subscriberMethod = (SubscriberMethod)i$.next();
            this.subscribe(subscriber, subscriberMethod, sticky, priority);
        }

接下来就是继续遍历我们刚才获取到的ArrayList对象,将得到的subscriberMethod 对象作为参数传入subscribe(subscriber, subscriberMethod, sticky, priority)这个方法,我们再去看看这个方法里面做了什么:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        Class eventType = subscriberMethod.eventType;
        CopyOnWriteArrayList subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventType);
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        if(subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList();
            this.subscriptionsByEventType.put(eventType, subscriptions);
        } else if(subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
        }

        int size = subscriptions.size();

        for(int subscribedEvents = 0; subscribedEvents <= size; ++subscribedEvents) {
            if(subscribedEvents == size || newSubscription.priority > ((Subscription)subscriptions.get(subscribedEvents)).priority) {
                subscriptions.add(subscribedEvents, newSubscription);
                break;
            }
        }

        Object var14 = (List)this.typesBySubscriber.get(subscriber);
        if(var14 == null) {
            var14 = new ArrayList();
            this.typesBySubscriber.put(subscriber, var14);
        }

        ((List)var14).add(eventType);
        if(sticky) {
            Map var11 = this.stickyEvents;
            Object stickyEvent;
            synchronized(this.stickyEvents) {
                stickyEvent = this.stickyEvents.get(eventType);
            }

            if(stickyEvent != null) {
                this.postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
            }
        }

    }

还好这个方法没有那么长,让我们来分析一下:
首先创建了一个Subscription对象,将之前封装对象里面的eventType(方法参数的Class 对象)作为键,创建一个List集合作为值添加到了一个concurrentHashmap对象subscriptionsByEventType中,后序就是通过这个对象来查找我们想要调用的方法并通过反射调用(这里只是先提一下)。
之前有几个参数我们没看懂,比如priority,当传入相同参数类型有多个方法的时候,将刚生产的Subscription按优先级的大小来确定位置添加到List集合当中。


        Object var14 = (List)this.typesBySubscriber.get(subscriber);
        if(var14 == null) {
            var14 = new ArrayList();
            this.typesBySubscriber.put(subscriber, var14);
        }

        ((List)var14).add(eventType);
        if(sticky) {
            Map var11 = this.stickyEvents;
            Object stickyEvent;
            synchronized(this.stickyEvents) {
                stickyEvent = this.stickyEvents.get(eventType);
            }

            if(stickyEvent != null) {
                this.postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
            }
        }

将方法参数的Class对象添加到var14 就是一个List集合中,并将这个List集合作为值和传入的subscriber作为键添加到一个map集合中typesBySubscriber。

2. 发布 Post(Object event)

  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");
            }

            try {
                while(!eventQueue.isEmpty()) {
                    this.postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }

    }

让我们来分析一下发布方法里面做了些什么:
1.将post(Object event)参数event对象添加到一个list集合。
2.第6行判断当前是否是UI线程,并且把isPosting属性设置为true。
3.然后会走到一个循环当中 当list集合不为空,会调用postSingleEvent方法,好吧 我们去看下这个方法。
4.最后会吧 isPosting和isMainThread都设置为false。

 private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error {
        Class eventClass = event.getClass();
        boolean subscriptionFound = false;
        if(this.eventInheritance) {
            List eventTypes = this.lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();

            for(int h = 0; h < countTypes; ++h) {
                Class clazz = (Class)eventTypes.get(h);
                subscriptionFound |= this.postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = this.postSingleEventForEventType(event, postingState, eventClass);
        }

        if(!subscriptionFound) {
            if(this.logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }

            if(this.sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
                this.post(new NoSubscriberEvent(this, event));
            }
        }

    }

首先看到这个方法lookupAllEventTypes(eventClass)

 private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) { Map var2 = eventTypesCache; synchronized(eventTypesCache) { Object eventTypes = (List)eventTypesCache.get(eventClass); if(eventTypes == null) { eventTypes = new ArrayList(); for(Class clazz = eventClass; clazz != null; clazz = clazz.getSuperclass()) { ((List)eventTypes).add(clazz); addInterfaces((List)eventTypes, clazz.getInterfaces()); } eventTypesCache.put(eventClass, eventTypes); } return (List)eventTypes; } }

这个方法会得到一个List集合,里面装的是我们pos传入参数event对象的Class,以及他的父类和接口的Class对象。
接着会遍历这个List集合,获得其中的每一个Class对象,然后调用了postSingleEventForEventType(event, postingState, eventClass)方法,再进入这个方法

 private boolean postSingleEventForEventType(Object event, EventBus.PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList subscriptions;
        synchronized(this) {
            subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventClass);
        }

        if(subscriptions != null && !subscriptions.isEmpty()) {
            Iterator i$ = subscriptions.iterator();

            while(i$.hasNext()) {
                Subscription subscription = (Subscription)i$.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;
                }
            }

            return true;
        } else {
            return false;
        }
    }

subscriptionsByEventType 有没有很熟悉啊??? 对了就是之前那个存放方法参数的Class对象为键,Subscriptions为值得那个concurrentHashmap对象,这个通过这个对象将post(Object event)参数的Class对象作为键的值取出来,这如果这个event的Class对象跟之前那个方法参数的Class对象是同一个对象,那么取出来的不就是之前存的Subscriptions对象吗?而Subscriptions集合里面装着newSubscription对象,newSubscription对象里面封装了所有需要的信息,在看postToSubscription(subscription, event, postingState.isMainThread)方法

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch(EventBus.SyntheticClass_1.$SwitchMap$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);
        }

    }

当threadMode枚举为1是说明找到的是onEvent()方法,并利用反射调用;当threadMode为2的时候说明找到的是onEventMainThread()方法,然后判断是否是主线程,如果是主线程就反射调用,如果不是,就将放入队列中。当threadMode为3的时候说明找到的是onEventBackgroundThread()方法,同样判断发送事件如果是在主线程,就将其放入队列,如果是子线程就直接利用发射调用,当threadMode为4的时候说明是调用onEventAsync()方法,它会将其放入队列并在合适的时候反射调用。

好了,到这里EventBus的源码大致分析清楚了,但还有一小部分没有分析,比如boolean sticky这个参数是干什么用的?registerSticky()这个构造函数有什么不同?下次再跟大家一起分析了。