EventBus 源码分析

EventBus 源码分析

先上github 地址:eventbus

首先,说说Eventbus 是什么,能干什么

其为android中的事件总线框架,用于在app内不同模块间通信消息传递的框架。简单的说,就是一个地方发送一个广播,在注册了该广播的地方接受到广播(在对应的函数中)。

怎么使用?

1.需要创建一个类,该类对应着一类事件。(相当于广播中的,意图过滤器中的条件)

class MyEvent{} // 代表一类事件。 即一类的广播。

2.在需要相应的类中,进行注册。(对应的要在该类销毁的时候,进行注销操作)

EventBus.getDefault().register(this);// 注册该类为接受者
EventBus.getDefault().unregister(this);// 注销该类。

3.在注册的类中,添加对应接受事件的函数。比如

public void onEvent(MyEvent event) {
// TODO 在接收到该事件时,我们要进行的相应处理。
}

4.发送一个事件。也可以理解为发送一个广播。

EventBus.getDefault().post(new MyEvent());//  发送的MyEvent对象,会被注册了该事件的方法中得到回调。

简单的用法就是这些了。

eventbus 源码

我们就从其暴露给我们的接口来一步步看起源码。

首先看从getDefault方法开始。

// 这里可以看到一个典型的创建的单例
public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
  }

在eventbus的构造函数中

EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();//   key 为所以Event的class,value为          该注册了该event的所以类。
        typesBySubscriber = new HashMap<Object, List<Class<?>>>();   // key为注册event的类,value为该类中的所有event的class。  以上两         个集合非常重要,会频繁出现在下面的分析中。
        stickyEvents = new ConcurrentHashMap<Class<?>, Object>(); // 保存粘性事件的集合。 key为event.class  value为该class对应的对象实例。
        // 以上三个map用来存放我们注册的事件相关的数据。
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        // 以上三个类,用来实现在不同线程中回调onEvent()函数。第一个为在通过handler实现主线程的回调。 后两个都是runnable的子类,是实现在子         线程中回调。
        subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);// 该类为一个功能性辅助类,在该         类中主要实现在一个类中,查找出所以得 onEvent方法。
        // 以下为以下简单的配置信息设置。
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

然后我们在看一下register(Object subscriber)方法,都会调用一个方法。

//subscriber 为注册的类,sticky 是否是粘性事件,priority为优先级 ,会影响存入map的顺序,进而影响回调的顺序。
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);
        }
    }

首先,我们看到首先调用了SubscriberMethodFinder的findSubscriberMethods方法。给类做的事情就是 把 订阅了事件的类中的所有的onEvent回调函数给返回出去。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods; 
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);// methodCache 为一个static变量,注册的类为key,以该类中所有注册函数的list为value,相当于一级缓存。将所有的注册类中的注册方法保存下来。避免多次遍历类中方法的操作。
        }
        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)) { // 以OnEvent开始的方法
                    int modifiers = method.getModifiers();        
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //访问修饰符判断 public且非Ignone方法
                        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 SubscriberMethod类为封装了一个类中接收函数信息的封装类。其中包含了方法method、该方法需要回调的线程threadmode、对应的事件类型eventtype。
                                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(); // 获取父类的class,继续循环
        }
        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;
        }
    }

然后,遍历得到集合,调用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法。该方法 主要功能就是为了将我们在EventBus 构造函数中创建的subscriptionsByEventType、typesBySubscriber和stickyEvents这三个集合,将对应的数据保存在他们中。前面已经说过他们对应保存的数据,这里就不重复了。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        Class<?> eventType = subscriberMethod.eventType; // 事件的对应的class
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 先从集合中获取以前保存的。
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);// subscription 类为对接受事件的类的包装类,里面包含 subscribe        r 就是我们register(Object o)时传入的this。一般就是activity或fragment。subscribermethod 就是该类中的onevent方法的封装,priority
        if (subscriptions == null) {//如果为空,代表第一次处理该event。创建list,并且将list添加到subscriptionsByEventType中。
            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)
        //将newSubscription按照优先级添加到中。
        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;
            }
        }// 到此,subscriptionsByEventType这个集合填充完毕。
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//同样的,先将这个类中之前event.class的集合取出。
        if (subscribedEvents == null) {  // 如果为空,代表是第一次,创建list集合,并且将其放到typesBySubscriber这个map中
            subscribedEvents = new ArrayList<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);// 将event加到刚才创建的list中。
        if (sticky) {  // 是否是粘性事件。如果不是的话,register方法到此就结束了。如果是注册的是粘性事件监听,就会在注册的时候,执行以下代码。
            Object stickyEvent;
            synchronized (stickyEvents) {
                stickyEvent = stickyEvents.get(eventType); // 该集合中的stickyEvent是在postSticky(Object o)的时候,添加到集合中的。所以所谓的粘性事件就是在这类                                                           注册粘性事件监听之前,就已经被post过的事件,在注册的时候会被直接调用,并将之前的Object作为参数传入。
            }
            if (stickyEvent != null) {
                // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
                // --> Strange corner case, which we don't take care of here.
                postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); // 这里是post粘性事件。该方法是post事件的核心方                法。之后在分析post模块的时候,会继续分析。
            }
        }
    }

到这里,我们就算走完了全部register的流程。我们现在回顾一下注册所做的事情。其实,最主要的事情就是将注册的类(比如activity)中的对应的onevent回调回调函数(比如 OnEvent(MyEvent myevent)),以及其参数,通过自己定义的包装类,保存到相应的集合之中(subscriptionsByEventType,typesBySubscriber)。

然后,我们再看一看,我们是如何post一个事件是如何实现的。

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();//获得在和该线程有关的postingState对象。
        List<Object> eventQueue = postingState.eventQueue;   // 将event添加到postingState中的list中。
        eventQueue.add(event);
        if (!postingState.isPosting) { //根据postingState的状态,设置一下相应的状态。
            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()) { // 将eventQueue中所有的对象post出去。
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

首先,我们先看一下PostingThreadState这个类。它是EventBus的内部类。其中主要是保存事件的一些信息,就是一个bean对象。这里需要特别关注一下currentPostingThreadState这个对象。它是ThreadLocal的实例。ThreadLocal可以理解一个用于在不同线程中保存数据的一个容器。比如你在主线程中添加了一条数据,又在子线程中添加了一条数据,然后,当你在主线程中去get的时候,拿到的就是你之前在主线程中保存的数据,如果你在子线程中去get,那么拿到的就是你在子线程中保存的数据。对于具体如何实现的,有兴趣的同学可以自己去研究研究,其中有用到很巧妙的算法。

然后我们在看看postSingleEvent(Object event, PostingThreadState postingState)方法.

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass(); // event对应的class
        boolean subscriptionFound = false;  
        if (eventInheritance) {   // 代表是否将event的父类,也进行处理。
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);  // 在该方法中,通过循环,将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);
        } 
}

然后,其都会调用postSingleEventForEventType方法,返回的boolean代表是否存在对应的注册者类。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) { // 这里我们看到,我们通过eventClass从subscriptionsByEventType中拿到我们之间在注册的时候添加进去的该event的所对应的注册者的集合。
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {// 遍历该event 所有的注册者
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;  // 将信息保存到postingState对象中
                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;
    }

在这里然后调用 postToSubscription方法。该方法在之前就有遇到过。实在register时,对于粘性事件我们调用了该方法。现在我们就来看看到底该方法干了什么。

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的类型,以及当前方法执行的线程(也就是post方法调用的时候的线程),来调用不同方法。

这里主要有四种方式。 一种是invokeSubscriber(subscription, event) (其实最后调用的都是这个方法,不过是在不同线程中调用而已)代表在当前线程中调用。第二种 mainThreadPoster.enqueue(subscription, event),在主线程中调用。第三种 backgroundPoster.enqueue(subscription, event) 会在子线程中调用,而且该线程是维护了一个队列,依次处理所有的加入的数据。第四种是asyncPoster.enqueue(subscription, event)会开辟新的子线程在其中执行。

正如我刚才说的,其实最后调用的都是invokeSubscriber(Subscription subscription, Object event)这个方法。 只是通过在不同线程中调用,来实现在不同线程中回调。

void invokeSubscriber(Subscription subscription, Object event) {
      subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
     // 实现功能的就是该方法,通过反射调用该方法。 
     .......
    }

现在我们来看看如何实现在不同线程中调用该方法。如果还记得的话,我们在eventbus的构造函数中,曾经创建过三个对象

mainThreadPoster,backgroundPoster,asyncPoster这三个对象,对应于不同线程。

首先,mainThreadPoster其实就是一个handler,而且是主线程中的handler,所以其对应的handleMessage方法会在主线程中执行。

首先调用了enqueue(Subscription subscription, Object event)方法。

首先,我们看PendingPost类,该也是一个封装event和subscription的类,但是不同的是其中包含一个pendingPostPool的存储集合,该集合里面添加进去的是我们之前创建过的PendingPost的对象。其作用是复用以前创建过的对象,减少对象创建的开销。

static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;       // 将使用完毕的PendingPost中的数据清空
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost); 添加到集合中
            }
        }
    }

在我们需要一个新的PendingPost的对象的时候

static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) { //如果集合中有不用的PendingPost,将其取出,并且将对应的数据添加到其中。
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

然后,回到enqueue方法中,

void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); // 获得封装了subscription和event的pendingpost对象
        synchronized (this) {
            queue.enqueue(pendingPost); // 将其添加到自己定义的队列中
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) { // 调用sendmessage方法发送一个空消息,会调用handler的handlemessage方法中。
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

然后,来到handlemessage方法

public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis(); //获取系统时间,相当于进入循环是的时间,因为我们看到下面是一个死循环,会用这个时间做一个限制。
            while (true) {
                PendingPost pendingPost = queue.poll();  //从队列中取出在enqueue中添加的数据。
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }  // 以上部分是为了判断队列是否为空的考虑线程安全的方法。
                eventBus.invokeSubscriber(pendingPost); // 这里,就是实现真正功能的代码。我们看到,它又回到了EventBus类中,只不过,它是在主线程中调用的。因为这是在主线程的handler的handlemessage方法中。
                long timeInMethod = SystemClock.uptimeMillis() - started; //又获取了一次系统时间,通过时间差,来控制循环。(当队列中数据好多时,才会用到)
                if (timeInMethod >= maxMillisInsideHandleMessage) {   
                    if (!sendMessage(obtainMessage())) {  //如果此次循环已经超过规定时间,发送一个消息,会重新调用handlemessage方法。
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

我们再回到eventbus中看看invokeSubscriber方法

void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription; // 将封装在PendingPost中的数据取出来。
        PendingPost.releasePendingPost(pendingPost);// 会将PendingPost对象中的数据清空,放到其内部的集合中,以便将来再用。
        if (subscription.active) {
            invokeSubscriber(subscription, event);// 该方法,以前已经分析过了,就是通过反射回调到相应的方法中,完成post的实现。
        }
    }

到这里,我们就看完了在主线程中实现回调的实现了。对于BackgroundPoster和AsyncPoster其实现是类似的,唯一的区别就是其是实现Runnable接口,其是运行在子线程中的。其中也是先将数据添加到一个队列中,然后再其run方法中调用eventBus.invokeSubscriber(pendingPost)方法。这两个的区别就是 一个线程安全,一个线程不安全。

到此。post方法我们也算分析完毕了。对于unregister()方法,如果前面的都看懂了,那么unregister就很简单了,就是将之前register时保存到集合中的数据删除掉就好了。这里就不再累赘了。相信大家都可以轻松看懂的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值