深入分析EventBus3.1,解析EventBus线程投递原理

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

EventBus作为广泛使用的Android端的通信总线,它支持高并发的工作环境,它在安卓端的使用口碑很好,笔者平时喜欢看看源码,这次就带大家一起分析EventBus的源码,一起学习。

项目地址:EventBus

官方已经介绍了项目的引用和使用,我就不说了,我们进入分析流程。

带着问题去看源码

带着问题去看源码是我一直比较推荐的。在查看源码之前我想了解一下几个问题

1,eventBus是支持activity与Service通信的,那它是如何封装的才让使用如此简便?

2,eventBus是如何把event投向目标线程的?

3,eventBus3.0在去掉对方法命名的限制后,它是如何查找订阅方法并正确响应方法感兴趣的参数类型的?

一边看源码,一边画类图

看源码时画类图也是我比较推荐的学习开源项目的方式,这可以加深你对项目在架构层面认识,更深刻理解项目,另外画了类图后,如果你过段时间再看项目可以更快的理解。


下面这个图是我在看eventBus源码时简画的,也没严格按照UML类图的标准画,但是能加深自己理解,大家也可以试试


源码分析

eventBus中的类并不算多,但是从哪说起呢?我们先从几个简单类的设计开始,然后再说到包含它们的复杂类,顺藤摸瓜探索整个EventBus的架构设计思路。

1,SubscriberMethod

这个类从名称上就很简单明了,代表订阅的方法

类中有如下几个属性

    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

属性的意义不难理解,其中method就是这个类代表的那个方法,threadMode是我们设置的线程模式。因为EventBus3.0在使用上是post了一个Object后,只会响应接收同类型参数的方法,所以我们可以猜到EventBus一定保存了订阅方法的参数类型信息,这儿我们发现了eventType属性,这个属性就是做这个事情的。priority是我们可以设置的优先级。sticky也是我们可以设置的支持粘性事件的属性。这个类还有简单的3个方法,只是起到辅助作用,就不说明了。

2,Subscription

此类仅有三个属性

final Object subscriber;
    final SubscriberMethod subscriberMethod;
    /**
     * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
     * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
     */
    volatile boolean active;

可见它用于描述一个订阅关系

3,PendingPost

final class PendingPost {
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

}

注意下此类中声明的static修饰的pendingPostPool,显然它是用来缓存PendingPost的。

4,PendingPostQueue

final class PendingPostQueue {
    private PendingPost head;
    private PendingPost tail;

    synchronized void enqueue(PendingPost pendingPost) {
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        if (tail != null) {
            tail.next = pendingPost;
            tail = pendingPost;
        } else if (head == null) {
            head = tail = pendingPost;
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        notifyAll();
    }

    synchronized PendingPost poll() {
        PendingPost pendingPost = head;
        if (head != null) {
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        return pendingPost;
    }

    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        if (head == null) {
            wait(maxMillisToWait);
        }
        return poll();
    }

}

它是一个简单的保存PendingPost的队列,有入列和出列方法


EventBus中的三个Poster算是很核心的类,这三个类决定了把不同线程的事件投递到它们应该执行的线程中

5,BackgroundPoster

从名称上我们可以知道,这个Poster负责把事件投递到后台线程中执行

final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

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

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

BackgroundPoster的代码不多,但是很重要,它里面的三个属性分别是之前讲过的事件队列,全局唯一的EventBus对象和是否正在执行的标识

在enqueue方法中,把事件放入队列中后,如果当前没有执行,就会执行

eventBus.getExecutorService().execute(this);

Poster继承了Runnable,所以会执行run方法,run方法就是取出一个事件然后调用

eventBus.invokeSubscriber(pendingPost);

执行。我们要注意的是eventBus.getExecutorService()

从名字上大家应该想到是一个线程池,其实它就是eventBus中的线程池,我们看看它到底在哪执行的

ExecutorService getExecutorService() {
        return executorService;
    }
executorService = builder.executorService;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
我们看到,BackgroundPoster中run方法的执行真正是在一个线程池中执行的,所以它能做到后台执行。


6,AsyncPoster

class AsyncPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

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

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

异步执行线程其实和BackgroundPoster的一样,也是把事件处理放在线程池中执行的,这个处理和Async是一致的,因为这个模式是把事件的执行线程和投递线程放在两个线程里,而线程池的执行线程是在线程池中找到一个空闲的缓存线程执行任务,所以就算你事件投递是在线程池的线程中投递的,且恰巧处理线程又是投递线程的缓存,但是线程池的线程上下文已经改变了,那前后也不是在一个环境执行。

7,MainThreadSupport

EventBus中保持着一个属性名为mainThreadPoster的常量,从名称上看它是负责把事件投递到主线程的,但是它仅仅是个Poster接口类型,它的创建是依赖与MainThreadSupport类的

mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

mainThreadSupport类的创建由builder代理

MainThreadSupport getMainThreadSupport() {
        if (mainThreadSupport != null) {
            return mainThreadSupport;
        } else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
            Object looperOrNull = getAndroidMainLooperOrNull();
            return looperOrNull == null ? null :
                    new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
        } else {
            return null;
        }
    }
这里面把获取的主线程的Looper传进创建方法里
public interface MainThreadSupport {

    boolean isMainThread();

    Poster createPoster(EventBus eventBus);

    class AndroidHandlerMainThreadSupport implements MainThreadSupport {

        private final Looper looper;

        public AndroidHandlerMainThreadSupport(Looper looper) {
            this.looper = looper;
        }

        @Override
        public boolean isMainThread() {
            return looper == Looper.myLooper();
        }

        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);
        }
    }

}

这个类很简短,里面的isMainThread方法是通过判断当前线程中的Looper是不是主线程的Looper确定的。

而创建Poster是创建了一个HandlerPoster对象

8,HandlerPoster

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

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

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

这个类是真正的主线程事件投递Poster,构造函数中有Looper,和一个最大执行时间限制,这是因为在主线程中要避免执行耗时任务。另外值得注意的是,此类继承了Handler,但是Handler的作用仅仅是在内部使用了sendMessage和handleMessage,Handler并没有对外部暴露,外部无法感知handler的存在,handleMessage中还是把事件投递到EventBus中的invokeSubscirber方法中的,所以,这里的Handler仅仅起到了发起处理事件请求的作用。

说完这8个类,主要是为了我们分析EventBus这个类做铺垫,其实铺垫并没有打完,因为有些类脱离事件执行过程而单独说一遍没有意义,所以一些类我们在分析EventBus时继续说

分析EventBus的三条思路

1,register和unrgister的过程

2,普通事件投递过程

3,sticky事件投递过程


register过程

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

register的过程简单的说,就是找到类中的订阅方法,然后再保存subscriber和subscriberMethod的信息,但是这里的信息量很大,我们先说说regitser的找寻订阅方法的过程


register中找寻方法的过程

我们可以看到register使用到了一个辅助类,SubscriberMethodFinder,这个类是EventBus中的核心类之一,它封装了查找订阅方法的功能

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

我们可以看到,找寻订阅方法有两种,一种是通过反射,对应了findUsingReflection,一种是通过注解器生成的信息查找,对应方法为findUsingInfo

我们先分析通过反射找方法的过程

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

方法中使用到了FindState类,这个类是用于记录查找过程的状态,并最终从它内部取出找到的所有方法,在其中的while循环中,先找到目标类中的订阅方法,然后再到目标类的父类中寻找,一直到遍历完成。

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

方法中,首先获取了目标类中声明的方法methods,然后在for循环中,把非public方法和带有之前声明的要忽略的修饰词的方法过滤掉了,所以这是为什么订阅方法必须为public,不能用static等修饰的原因,另外代码中可以看出,订阅方法只能携带一个参数

在遍历中,如果方法带有预期的注释,就通过findState的checkAdd方法检测有没有被加入,如果没有,就存在findState中,我们看看findState中检测和添加的过程

boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            } else {
                if (existing instanceof Method) {
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

上述代码是检测的过程,我们看到,FindState中缓存了找到的订阅方法的信息,这个缓存的Map把eventType定为key,这么做的原因在注释里也说的很明白,因为一个类中不应该存在多个方法订阅一种事件类型的情况(这么做确实没意义啊),所以这里的检测逻辑是如果之前没有已经缓存的对应事件类型的订阅方法,就直接返回true,如果发现已经有了订阅这个事件类型的方法,那么EventBus就抛出一个错误,提醒你重复订阅了相同类型事件。

方法中用到的checkAddWithMethondSignature是通过检测方法的签名加参数类型来判断是不是之前已经添加的方法,这个大家可以自己看看,这里就不介绍了。

找寻到的方法都会被添加到findState中

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

最后获取到的所有保存在FindState中的方法会在findUsingReflection中通过调用下面方法被返回

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

可见其中findState对象也会被去除内部引用并放入FindState缓存池中

在findSubscriberMethods方法中,获取到的方法被放入缓存METHOD_CACHE中

这就是通过反射寻找订阅方法的过程,通过注解器生成的信息寻找订阅方法的过程类似,我就不单独说了,大家可以看看源码。

但是我们还没说完register过程,register中获取了订阅方法后通过调用subscribe方法把订阅者和订阅方法做了绑定,并且把订阅类存在了typesBySubscriber中。另外方法里面有对sticky事件的处理逻辑,如果是订阅了sticky事件,就遍历有没有符合的sticky事件,如果有,就投递事件,处理逻辑在checkPostStickyEventToSubscription方法中

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            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 i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        if (subscriberMethod.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);
            }
        }
    }

checkPostStickyEventToSubscription方法里掉用了postToSbscription,我们先不看这个方法,因为在分析事件投递过程还会说道这个方法

那么说道这里,register的过程就算分析完毕了,unregister的过程非常简单,直接把typesBySubscriber中缓存的订阅类删掉并解除订阅关系就可以了

public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

其中解除订阅关系是在unsubscribeByEventType方法

private void unsubscribeByEventType(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--;
                }
            }
        }
    }

方法中去除了缓存中已经存在的订阅关系,并且把相关事件类型的active属性设为false,这样在queue中遍历到这些事件时就会丢弃掉





普通事件投递过程

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            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;
            }
        }
    }
其中值得注意的是currentPostingThreadState,它保存着每个每个线程对应的事件队列和投递信息
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

事件的投递进入到postingSingleEvent方法

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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

方法中,如果eventInheritance为true,也就是如果一个事件A是继承另一个事件B,那么A事件也要发送给订阅了B事件的订阅者

所以eventInheritance为true时有一个循环,不过投递经过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;
    }

我们看到再经过一些判断后,事件投递的过程进入了postToSubscription方法,也就是我们之前提到的方法,我们下面分析这个方法

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                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);
        }
    }

这个方法是负责把事件投递到目标线程的,如果是POSTING类型,即在投递线程里执行,则直接执行,应为这是从post方法过来了,所以就是在投递线程里

如果是MAIN,即主线程类型,如果是在主线程,则直接执行,否则投递到我们之前了解的mainThreadPoster中

如果是MAIN_ORDERED类型,也是在主线程执行,但是不会立刻执行,而是进入队列等待

如果是BACKGROUND类型,如果不是主线程则直接执行,否则放入backgroundPoster中

如果是ASYNC类型,则直接放入asyncPoster中

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

postToSubscription中多处调用了invokeSubscriber方法,invokeSubscriber只是简单调用了订阅方法

普通事件的投递已经说完了



sticky事件投递过程

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

sticky事件的一个重要特性就是它在首次发送之后,如果有新的订阅者订阅了相同类型的sticky事件,那么之前发送否sticky事件还会被传送到新的订阅者那里,这是因为sicky事件被缓存到了stickyEvents里面。被缓存的sticky事件在有新的订阅者订阅了sticky事件后被遍历,如果有新订阅者感兴趣的事件,这把事件投递到新订阅者那

写到这整个EventBus的分析算是完成了,读者应该能够回答之前提出的三个问题了


总结

1,EventBus的实现可以看成是经典的观察者模式,注册的订阅者就是观察者,他们被保存在EventBus中,当有事件需要投递时,则把观察者感兴趣的事件投递给它们

2,EventBus在不同线程的执行效果是通过线程池实现的

3,EventBus判断一个订阅者是否对一个事件感兴趣是从依据事件类型是否和订阅方法的参数类型一致判断的

4,EventBus中是把订阅者和订阅方法做了很好的封装,它内部引用这些订阅者,所以它可以拿到Activity和Service的引用,这样用于两者通信很方便

写在后面

我并不擅长写文章,包括技术类文章,在写这篇文章时总感觉有些虎头蛇尾的感觉,流程的分析也像是过一遍代码的感觉,但是这是我自己读源码时的过程,显得有些枯燥,代入感不强。这些问题我会尝试改进。另外我自己也看过一些开源项目,很多是UI框架,但是之前我分析第三方框架的记录都写在有道云笔记里了,所以我准备把我一些分析时学到的知识分享出来,读者可以关注下我的博客,这样可以获取最新的第三方框架分析,大家一起学习

猜你喜欢

转载自blog.csdn.net/tingfengzheshuo/article/details/79487257