EventBus1.0.1源码解析

很久没有更新过源码解析类文章,以下内容作为源码分析类的笔记。分析方法适用于其它源码分析。

分析工具说明

许久以来,阅读源代码最得力的工具就非Source Insight莫属了。然,后来微软出了一款轻量级但功能强大的IDE就没Source Insight什么事了。微软的这款IDE就是大名鼎鼎的VSCODE,全名叫:Visual Studio Code。它的强大之处这里就不过多描述了。我们转回到正题。

获取源码并导入IDE

我们在Github.com上搜索到EventBus的项目地址,然后进行克隆即可。

    git clone https://github.com/greenrobot/EventBus.git

克隆完成之后,导入IDE。打开VS CODE,在菜单栏中选择File -> Open,选择克隆后文件所在的目录,然后点击确认。这就完成了代码导入工作。

由于我们只是分析EventBus的骨架脉络,所以我们要选择EventBus的早期版本。这里我们将要选择V1.0.1版。这同样适用于Android源代码分析,适宜选择1.5版或1.6版。

切换版本的步骤为:先进入EventBus刚才的克隆目录,然后执行以下步骤:

    //列出EventBus项目的所有tag。
    sahadevs-MacBook-Pro:EventBus sahadev$ git tag
    V1.0.1
    V2.2.0
    V2.3.0
    V2.4.0
    V3.0.0
    //上面可以看出EventBus共发布了5个版本。我们切换到V1.0.1版。
    sahadevs-MacBook-Pro:EventBus sahadev$ git checkout V1.0.1

经过以上步骤,我们的代码就切换到了V1.0.1版本。VS CODE中的内容也会同步更新。接下来开始我们的代码分析。

事件监听注册

我们熟知EventBus的用法,其中最有代表性的入口为:

     EventBus.getDefault().register(this);

为了方便分析,上面这行代码放在Activity中执行。所以上面的this就是Activity的实例。EventBus采用了单例模式,在这个版本中,采用了最简便的实现方式:

public class EventBus {
    private static final EventBus defaultInstance = new EventBus();

    public static EventBus getDefault() {
        return defaultInstance;
    }
}

好,以上并不是我们的重点。我们要从register方法说起。

    public void register(Object subscriber) {
        register(subscriber, defaultMethodName, ThreadMode.PostThread);
    }

    public void register(Object subscriber, String methodName, ThreadMode threadMode) {
        List<Method> subscriberMethods = findSubscriberMethods(subscriber.getClass(), methodName);
        for (Method method : subscriberMethods) {
            Class<?> eventType = method.getParameterTypes()[0];
            subscribe(subscriber, method, eventType, threadMode);
        }
    }

register(this)方法执行时调用了register(Object subscriber, String methodName, ThreadMode threadMode),subscriber是Activity,methodName采用了defaultMethodName的值,这个值在EventBus中定义为:’onEvent’,threadMode为ThreadMode.PostThread。其中ThreadMode为一个枚举,它有两个值:

    public enum ThreadMode {
        /** Subscriber will be called in the same thread, which is posting the event. */
        PostThread,
        /** Subscriber will be called in Android's main thread (sometimes referred to as UI thread). */
        MainThread,
        /* BackgroundThread */
    }

PostThread用于标识发送线程,MainThread用于标识主线程。它们在后期执行的区别为:如果在注册时为PostThread,则事件发送后,会直接调起监听方法。而如果注册时为MainThread,则会经过主线程的消息队列调起。

好,回到刚才的地方,在register(Object subscriber, String methodName, ThreadMode threadMode)方法内先执行了findSubscriberMethods,我们移步看看这个方法内执行了什么工作:

    private List<Method> findSubscriberMethods(Class<?> subscriberClass, String methodName) {
        ...
        List<Method> subscriberMethods;
        ...
        subscriberMethods = new ArrayList<Method>();
        Class<?> clazz = subscriberClass;
        HashSet<Class<?>> eventTypesFound = new HashSet<Class<?>>();
        while (clazz != null) {
            String name = clazz.getName();
            ...
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) {
                        if (eventTypesFound.add(parameterTypes[0])) {
                            // Only add if not already found in a sub class
                            subscriberMethods.add(method);
                        }
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        ...
    }

这里面的代码比较长,将不必要的略去。这段代码主要做了以下工作:

根据给定的类,遍历该类及其父类所有的方法,如果遍历时发现与给定的方法名相等,并且该方法的参数只有一个,那么就将该方法加入到subscriberMethods中,最后返回。这里被找到的方法是同名的,但是参数类型不同,这里的参数类型决定了将来要监听的事件类型。

好,我们回到findSubscriberMethods之前被调用的地方,继续往下走。接下来的动作是将findSubscriberMethods方法返回的Method列表进行遍历。而这次遍历时,将Method的第一个参数类型作为参数传给了subscribe方法:

    private void subscribe(Object subscriber, Method subscriberMethod, Class<?> eventType, ThreadMode threadMode) {
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<Subscription>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } 
        ...
        subscriberMethod.setAccessible(true);
        Subscription subscription = new Subscription(subscriber, subscriberMethod, threadMode);
        subscriptions.add(subscription);
        ...
    }

该方法首先做的就是根据eventType从subscriptionsByEventType对象中取出对应的subscriptions,然这里第一次肯定是null。所以先进行实例化,然后存放于subscriptionsByEventType中。而subscriptionsByEventType对象存放的就是根据eventType所对应的subscriptions。而subscriptions中存放的就是所有的观察者,待会有时间发生时,根据事件的类型,从subscriptionsByEventType中取出所有的观察者subscriptions逐一通知。

在创建了观察者列表subscriptions之后开始创建具体的观察者。Subscription是一个基本的类,它的示例只是持有了subscriber(Activity),subscriberMethod(onEvent),threadMode(ThreadMode.PostThread)三者的引用而已。

到这里,观察者Activity监听某一事件的注册工作就算完成了。

事件发送

接下来,我们将从事件发送入口post方法开始分析:

    public void post(Object event) {
        //这里的两个属性很重要,下面这个用于缓存事件,待下次事件执行时,一并执行。
        List<Object> eventQueue = currentThreadEventQueue.get();
        eventQueue.add(event);

        //这个用于标识是否正在发送事件,保证事件的顺序发送。防止多线程情况下的交叉执行。
        BooleanWrapper isPosting = currentThreadIsPosting.get();
        if (isPosting.value) {
            return;
        } else {
            isPosting.value = true;
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0));
                }
            } finally {
                isPosting.value = false;
            }
        }
    }

这里最开始有两个EventBus的成员属性,这里很重要:currentThreadEventQueue与currentThreadIsPosting。这两个属性本身是和线程有关系的。关于这两个属性内部的作用机制这里就不再赘述了。上面post方法的第一行currentThreadEventQueue.get()返回的是一个空的ArrayList()对象。而currentThreadIsPosting.get()方法返回的是一个BooleanWrapper对象,它的默认属性value默认是false。如果对这块的返回值有疑问,可以在EventBus.java文件内找到这两个对象的定义,它们在new时,通过initialValue方法返回了get获取到的初始值。

通过判断,最终进入postSingleEvent方法内,而该方法则是事件分发的核心:

    private void postSingleEvent(Object event) throws Error {
        List<Class<?>> eventTypes = findEventTypes(event.getClass());
        ...
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                subscriptions = subscriptionsByEventType.get(clazz);
            }
            if (subscriptions != null) {
                for (Subscription subscription : subscriptions) {
                    if (subscription.threadMode == ThreadMode.PostThread) {
                        postToSubscribtion(subscription, event);
                    } else if (subscription.threadMode == ThreadMode.MainThread) {
                        mainThreadPoster.enqueue(event, subscription);
                    } else {
                        throw new IllegalStateException("Unknown thread mode: " + subscription.threadMode);
                    }
                }
                ...
            }
        }
        ...
    }

这个方法内首先调用了findEventTypes方法,我们先移步看看它做了什么工作:

    private List<Class<?>> findEventTypes(Class<?> eventClass) {
        ...
        Class<?> clazz = eventClass;
        while (clazz != null) {
            eventTypes.add(clazz);
            addInterfaces(eventTypes, clazz.getInterfaces());
            clazz = clazz.getSuperclass();
        }
        return eventTypes;
    }

这部分的主要工作为:将指定类以及父类所实现的接口及其本身的类型加入到eventTypes中,最后将eventTypes返回。如果我们在定义事件类型时,没有继承任何类、实现任何接口,那么就会省去这个步骤,节省时间。

回到postSingleEvent方法继续往下看:根据刚刚返回的事件类型集合开始遍历,每次遍历时,根据给定的事件类型从subscriptionsByEventType中寻找对应的监听者。我们在上面分析注册部分时候已经见过,subscriptionsByEventType中存放的就是事件类型与监听者的键值对。找到监听者列表subscriptions之后,开始对subscriptions进行遍历,进行消息下发。

消息的发送主要两种模式:

  • PostThread
  • MainThread

我们在开头的时候就提到过,PostThread会通过反射直接调起对应的方法,而MainThread则会通过主线程的消息队列进行调起。我们来看看究竟是不是这样:

    //PostThread的执行方式
    static void postToSubscribtion(Subscription subscription, Object event) throws Error {
        ...
        subscription.method.invoke(subscription.subscriber, event);
        ...
    }

    //MainThread的执行方式
    void enqueue(Object event, Subscription subscription) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(event, subscription);
        Message message = obtainMessage();
        message.obj = pendingPost;
        if (!sendMessage(message)) {
        throw new RuntimeException("Could not send handler message");
        }
    }

可以看到:对于PostThread类型,直接通过反射将事件传给了观察者。而对于MainThread类型,则是通过Handler将信息传给了主线程。最后消息到达主线程后,通过与PostThread方式一样经过postToSubscribtion方法执行。


以上就是关于EventBus事件监听注册、事件广播的解析过程。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页