EventBus框架库代码走读

来源:互联网 发布:mac游戏 编辑:程序博客网 时间:2024/05/16 17:33

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊!

【工匠若水 http://blog.csdn.net/yanbober】

本篇继续接上一篇,阅读上一篇EventBus使用之基础

背景

开始分析EventBus前可以下看下EventBus开源框架的工程目录结构:
这里写图片描述

从上图可以发现,其实EventBus的代码量不是很大,还是很方便入手分析的。

开始分析

通过上一篇基础使用可以发现,使用EventBus框架第一步是得到EventBus实例,那我们就从EventBus类文件的getDefault()方法开始分析。如下:

    /** Convenience singleton for apps using a process-wide EventBus instance. */    public static EventBus getDefault() {        if (defaultInstance == null) {            synchronized (EventBus.class) {                if (defaultInstance == null) {                    defaultInstance = new EventBus();                }            }        }        return defaultInstance;    }

这里就是设计模式的一个单例模式运用,使用了双重判断的方式,防止并发的问题,还能极大的提高效率。接着往下看EventBus()构造方法:

    public EventBus() {        this(DEFAULT_BUILDER);    }    EventBus(EventBusBuilder builder) {        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();        typesBySubscriber = new HashMap<Object, List<Class<?>>>();        stickyEvents = new ConcurrentHashMap<Class<?>, Object>();        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);        backgroundPoster = new BackgroundPoster(this);        asyncPoster = new AsyncPoster(this);        subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);        logSubscriberExceptions = builder.logSubscriberExceptions;        logNoSubscriberMessages = builder.logNoSubscriberMessages;        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;        throwSubscriberException = builder.throwSubscriberException;        eventInheritance = builder.eventInheritance;        executorService = builder.executorService;    }

这里的构造方法EventBus(EventBusBuilder builder)使用了建造者模式给EventBus设置一些基本参数标志。这里使用了DEFAULT_BUILDER,也即默认参数。

到这里一个默认的单例模式的EventBus对象实例获取完成。通过这段代码可以发现,我们也可以通过 EventBusBuilder或者EventBus的构造函数新建一个EventBus实例,但是每个新建的EventBus发布和订阅事件都是相互隔离的。

接着继续停留在EventBus类分析,可以看到该类的主要职责就是负责所有对外暴露的API。

按照上一篇的使用例子顺序来看,得到EventBus实例后需要在开始处调运register方法,结束处调运unregister方法,所以我们从register方法开始分析:

    public void register(Object subscriber) {        register(subscriber, false, 0);    }    public void register(Object subscriber, int priority) {        register(subscriber, false, priority);    }    public void registerSticky(Object subscriber) {        register(subscriber, true, 0);    }    public void registerSticky(Object subscriber, int priority) {        register(subscriber, true, priority);    }    private synchronized void register(Object subscriber, boolean sticky, int priority) {        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());        for (SubscriberMethod subscriberMethod : subscriberMethods) {            subscribe(subscriber, subscriberMethod, sticky, priority);        }    }

可以看到EventBus类提供了四个register方法,但是最终都调运了register(Object subscriber, boolean sticky, int priority)方法。

register(Object subscriber, boolean sticky, int priority)方法的三个参数含义如下:

  • subscriber:传入要扫描类的类对象(订阅者)。
  • sticky:是否是粘粘性消息。
  • priority:优先级,优先级越高,在调用的时候会越先调用。

由此可以看出register(Object subscriber),registerSticky(Object subscriber)的默认优先级都是0,其他的register方法可自定义优先级。同理register的方法默认都是不粘粘性的事件,registerSticky的方法没人都是粘粘性的事件。

继续看register(Object subscriber, boolean sticky, int priority)方法,可以发现其中首先执行了List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());语句,这句话就是调运SubscriberMethodFinder类的findSubscriberMethods方法,传入了subscriber 的class,返回一个List<SubscriberMethod>。如下:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {        String key = subscriberClass.getName();        List<SubscriberMethod> subscriberMethods;        synchronized (methodCache) {            subscriberMethods = methodCache.get(key);        }        if (subscriberMethods != null) {            return subscriberMethods;        }        subscriberMethods = new ArrayList<SubscriberMethod>();        Class<?> clazz = subscriberClass;        HashSet<String> eventTypesFound = new HashSet<String>();        StringBuilder methodKeyBuilder = new StringBuilder();        while (clazz != null) {            String name = clazz.getName();            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {                // Skip system classes, this just degrades performance                break;            }            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)            Method[] methods = clazz.getDeclaredMethods();            for (Method method : methods) {                String methodName = method.getName();                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {                    int modifiers = method.getModifiers();                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {                        Class<?>[] parameterTypes = method.getParameterTypes();                        if (parameterTypes.length == 1) {                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.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")) {                                threadMode = ThreadMode.Async;                            } else {                                if (skipMethodVerificationForClasses.containsKey(clazz)) {                                    continue;                                } else {                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);                                }                            }                            Class<?> eventType = parameterTypes[0];                            methodKeyBuilder.setLength(0);                            methodKeyBuilder.append(methodName);                            methodKeyBuilder.append('>').append(eventType.getName());                            String methodKey = methodKeyBuilder.toString();                            if (eventTypesFound.add(methodKey)) {                                // Only add if not already found in a sub class                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));                            }                        }                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."                                + methodName);                    }                }            }            clazz = clazz.getSuperclass();        }        if (subscriberMethods.isEmpty()) {            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "                    + ON_EVENT_METHOD_NAME);        } else {            synchronized (methodCache) {                methodCache.put(key, subscriberMethods);            }            return subscriberMethods;        }    }

上面就是获取传入对象class的方法的方法。其中前半部分是先去缓存查找是否有这个类的记录,如果有直接返回,没有继续执行。当没有时继续走到Method[] methods = clazz.getDeclaredMethods();语句得到该类的所有方法;接着那个大for循环就是遍历这个类匹配符合封装要求的method;其中,if (methodName.startsWith(ON_EVENT_METHOD_NAME))用来判断方法名是不是以“onEvent”开头;接着if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0)用于继续判断是否是public且非static和abstract方法;if (parameterTypes.length == 1)用于继续判断是否是一个参数。如果都复合,才进入封装的部分;接着也比较简单,根据方法的后缀,来确定threadMode,threadMode是个四种情况的枚举类型(前面基础使用一篇解释过四种类型);接着通过subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));把method添加到subscriberMethods列表;接着通过clazz = clazz.getSuperclass();扫描父类的方法;接着while结束,扫描完后通过methodCache.put(key, subscriberMethods);将方法放入缓存,然后返回List<SubscriberMethod>的方法列表(订阅者方法至此查找完成)。

接着继续回到上一级方法:

    private synchronized void register(Object subscriber, boolean sticky, int priority) {        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());        for (SubscriberMethod subscriberMethod : subscriberMethods) {            subscribe(subscriber, subscriberMethod, sticky, priority);        }    }

通过for循环遍历List<SubscriberMethod>里的方法,同时传入suscribe方法。具体如下:

    // Must be called in synchronized block    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {        //从订阅方法中拿到订阅事件的类型        Class<?> eventType = subscriberMethod.eventType;        ////通过订阅事件类型,找到所有的订阅(Subscription)        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);        //创建一个新的订阅        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);        if (subscriptions == null) {            //如果该事件目前没有订阅列表,创建并加入该订阅            subscriptions = new CopyOnWriteArrayList<Subscription>();            subscriptionsByEventType.put(eventType, subscriptions);        } else {            //如果有订阅列表,检查是否已经加入过            if (subscriptions.contains(newSubscription)) {                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "                        + eventType);            }        }        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)        // subscriberMethod.method.setAccessible(true);        int size = subscriptions.size();        for (int i = 0; i <= size; i++) {            //根据优先级插入订阅            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {                subscriptions.add(i, newSubscription);                break;            }        }        //根据subscriber存储它所有的eventType        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);        if (subscribedEvents == null) {            subscribedEvents = new ArrayList<Class<?>>();            typesBySubscriber.put(subscriber, subscribedEvents);        }        //将这个订阅事件加入到订阅者的订阅事件列表中        subscribedEvents.add(eventType);        //判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行。stickyEvent其实就是我们post时的参数        if (sticky) {            if (eventInheritance) {                // Existing sticky events of all subclasses of eventType have to be considered.                // Note: Iterating over all events may be inefficient with lots of sticky events,                // thus data structure should be changed to allow a more efficient lookup                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();                for (Map.Entry<Class<?>, Object> entry : entries) {                    Class<?> candidateEventType = entry.getKey();                    if (eventType.isAssignableFrom(candidateEventType)) {                        Object stickyEvent = entry.getValue();                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);                    }                }            } else {                Object stickyEvent = stickyEvents.get(eventType);                checkPostStickyEventToSubscription(newSubscription, stickyEvent);            }        }    }

到这里register方法分析完了,大致流程总结一下:

  1. 找到被注册者类中的所有的订阅方法。
  2. 遍历订阅方法,找到EventBus中eventType对应的订阅列表,然后根据当前订阅者和订阅方法创建一个新的订阅加入到订阅列表。
  3. 找到EvnetBus中subscriber订阅的事件列表,将eventType加入到这个事件列表。

所以对于任何一个订阅者,我们可以找到它的订阅事件类型列表,通过这个订阅事件类型,可以找到在订阅者中的订阅函数。

既然register函数分析完了,那么接下来就该分析unregister了,成对出现嘛!如下:

    /** Unregisters the given subscriber from all event classes. */    public synchronized void unregister(Object subscriber) {        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);        if (subscribedTypes != null) {            for (Class<?> eventType : subscribedTypes) {                unubscribeByEventType(subscriber, eventType);            }            typesBySubscriber.remove(subscriber);        } else {            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());        }    }

可以看到,首先获取了subscribe函数中根据subscriber存储它的所有eventType保存到List<Class<?>> subscribedTypes;接着如果存在注册过的type则通过unubscribeByEventType(subscriber, eventType);循环遍历,完事remove掉所有,这样就完成了所有的unregister功能;至于unubscribeByEventType函数如何实现,具体如下:

    /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */    private void unubscribeByEventType(Object subscriber, Class<?> eventType) {        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);        if (subscriptions != null) {            int size = subscriptions.size();            for (int i = 0; i < size; i++) {                Subscription subscription = subscriptions.get(i);                if (subscription.subscriber == subscriber) {                    subscription.active = false;                    subscriptions.remove(i);                    i--;                    size--;                }            }        }    }

从上面代码可以看出,原来在register时真正存储EventBus事件的Map是subscriptionsByEventType成员。这里就是循环遍历找出需要unregister的remove掉。至此,整个EventBus的register与unregister函数都分析完毕。

依照前一篇使用来看,进行完register与unregister后剩下的就是post了,那么接下来分析分析post过程,如下:

    /** Posts the given event to the event bus. */    public void post(Object event) {        PostingThreadState postingState = currentPostingThreadState.get();        List<Object> 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()) {                    postSingleEvent(eventQueue.remove(0), postingState);                }            } finally {                postingState.isPosting = false;                postingState.isMainThread = false;            }        }    }    public void postSticky(Object event) {        synchronized (stickyEvents) {            stickyEvents.put(event.getClass(), event);        }        // Should be posted after it is putted, in case the subscriber wants to remove immediately        post(event);    }

如上postSticky(Object event)的实质是post了一个stickyEvents,而真正的post(Object event)方法里,currentPostingThreadState是一个ThreadLocal类型的,里面存储了PostingThreadState;PostingThreadState包含了一个eventQueue和一些标志位;eventQueue.add(event);就是把事件放入eventQueue队列,然后while循环遍历eventQueue通过postSingleEvent(eventQueue.remove(0), postingState);语句分发事件;那继续看下这条语句的实现:

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {        Class<?> eventClass = event.getClass();        boolean subscriptionFound = false;        if (eventInheritance) {            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);            int countTypes = eventTypes.size();            for (int h = 0; h < countTypes; h++) {                Class<?> clazz = eventTypes.get(h);                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);            }        } else {            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);        }        if (!subscriptionFound) {            if (logNoSubscriberMessages) {                Log.d(TAG, "No subscribers registered for event " + eventClass);            }            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&                    eventClass != SubscriberExceptionEvent.class) {                post(new NoSubscriberEvent(this, event));            }        }    }

如上代码通过List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);语句传入eventClass得到eventClass对应的事件,包含父类对应的事件和接口对应的事件;接着通过循环遍历eventTypes执行subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);语句;最后如果发现没有对应事件就通过post(new NoSubscriberEvent(this, event));post一个NoSubscriberEvent事件;接下来看下postSingleEventForEventType函数的实现:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {        CopyOnWriteArrayList<Subscription> subscriptions;        synchronized (this) {            subscriptions = subscriptionsByEventType.get(eventClass);        }        if (subscriptions != null && !subscriptions.isEmpty()) {            for (Subscription subscription : subscriptions) {                postingState.event = event;                postingState.subscription = subscription;                boolean aborted = false;                try {                    postToSubscription(subscription, event, postingState.isMainThread);                    aborted = postingState.canceled;                } finally {                    postingState.event = null;                    postingState.subscription = null;                    postingState.canceled = false;                }                if (aborted) {                    break;                }            }            return true;        }        return false;    }

如上可以发现,我们在register时扫面class把匹配的方法都存储在了subscriptionsByEventType,这里通过subscriptions = subscriptionsByEventType.get(eventClass);语句首先拿到register时扫描的匹配方法;然后判断是否有匹配的方法,如果有就继续遍历每个subscription,依次去调用postToSubscription(subscription, event, postingState.isMainThread);
其实这个方法在register的subscribe的checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent)的方法中也调运过。所以我们继续来分析下这个方法,如下:

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {        switch (subscription.subscriberMethod.threadMode) {            case PostThread:                invokeSubscriber(subscription, event);                break;            case MainThread:                if (isMainThread) {                    invokeSubscriber(subscription, event);                } else {                    mainThreadPoster.enqueue(subscription, event);                }                break;            case BackgroundThread:                if (isMainThread) {                    backgroundPoster.enqueue(subscription, event);                } else {                    invokeSubscriber(subscription, event);                }                break;            case Async:                asyncPoster.enqueue(subscription, event);                break;            default:                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);        }    }

这个方法传入的三个参数含义分别是:第一个参数就是传入的订阅,第二个参数就是对于的分发事件,第三个参数表明是否在主线程;然后通过subscription.subscriberMethod.threadMode判断该在哪个线程去执行;这里通过switch分四种情况,如下:

  • case PostThread:直接在当前线程反射调用。

  • case MainThread:如果是(isMainThread)主UI线程则直接调用,否则把当前的方法加入到队列,然后直接通过handler去发送一个消息,通过Handler在主线程执行。

  • case BackgroundThread:如果当前不是主UI线程(!isMainThread)则直接调用,如果是UI线程则创建一个runnable加入到后台的一个队列,最终由Eventbus中的一个线程池去调用。

  • case Async:不论什么线程,直接丢入线程池,也就是将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。

  • default:抛出线程state状态非法异常。

继续分析可以发现mainThreadPoster是继承Handler实现的,其中Looper是MainLooper;invokeSubscriber与asyncPoster都是继承Runnable实现的,其中invokeSubscriber与asyncPoster的enqueue方法实质都差不多,如下:

    public void enqueue(Subscription subscription, Object event) {        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);        queue.enqueue(pendingPost);        eventBus.getExecutorService().execute(this);    }

可以验证上面说的,invokeSubscriber与asyncPoster的enqueue方法都是扔到了一个线程池中执行。好了,继续看下mainThreadPoster的enqueue方法,如下:

    void enqueue(Subscription subscription, Object event) {        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);        synchronized (this) {            queue.enqueue(pendingPost);            if (!handlerActive) {                handlerActive = true;                if (!sendMessage(obtainMessage())) {                    throw new EventBusException("Could not send handler message");                }            }        }    }

可以验证上面说的,mainThreadPoster的Looper是MainLooper,所以这里通过sendMessage(obtainMessage())将消息异步传入了主线程执行。

接下来看下上面switch中使用的invokeSubscriber方法:

    void invokeSubscriber(Subscription subscription, Object event) {        try {            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);        } catch (InvocationTargetException e) {            handleSubscriberException(subscription, event, e.getCause());        } catch (IllegalAccessException e) {            throw new IllegalStateException("Unexpected exception", e);        }    }

从上面可以看出invokeSubscriber方法实质就是invoke反射,对,就是java的反射调运,反射调运你onEvent开头的方法。

好了,到这里获取EventBus实例,register,unregister,post,onEventXXX的过程都分析完毕了。

总结

还是回到开源代码作者提供的这幅图:

这里写图片描述

这下明白了吧,EventBus实质就是观察者模式。说俗点通过分析和上图可以直观认为上图中EventBus椭圆是一个具备存储匹配方法的一个单例对象;右侧Subscriber块的onEventXXX就是这些匹配方法;左侧的post就是根据参数去查找反射调运右侧那些已定义的onEventXXX方法。

7 4
原创粉丝点击