EventBus3.0及以上源码解析

一、问题导入

    带着问题去看源码,我觉得更具有目的性和趣味性,这也是把这一块放在第一部分的目的。

1.1 在一个Activity onCreate()中注册EventBus,在onDestroy()中注销EventBus,但是没有订阅事件(@subscribe)有没有什么问题?

1.2 在同一个Activity中能不能申明两个方法订阅同一个事件?如果能,那发送事件后能接收几次事件?

1.3 如果A extends B 在A中订阅事件TestEvent,B中也订阅了事件TestEvent,发送事件后A、B都能收到事件吗?如果不能哪一个能够收到?

1.4 一般我们申明订阅方法类似public void onEvent(TestEvent event),然后添加注解,那么这个方法的申明修饰符能否为private static abstract ,参数能够大于1个吗?

1.5 我们一般获取一个EventBus 实例的时候,会采用EventBus.getDefault()。那么EvengBus 是不是严格意义上的单例,如果是,那么这样设计的目的是什么,如果不是那么这样设计的目的是什么?

1.6 粘性事件和普通事件的差异性?

1.7 HandlePoster 、BackgroundPoster、AsyncPoster 区别?@subscribe(threadMode) 订阅线程的差异?

二、框架设计脉络

EventBus3.0及以上源码解析_第1张图片

 

三、Register

    3.1 register主要完成两件事情:(1.查找订阅者订阅方法 2.订阅者注册)  

public void register(Object subscriber) {
    // 注册完成两件事情
    // 1.查找订阅者订阅方法  2.订阅者注册

    // 通过反射获取到订阅者Class对象
    Class subscriberClass = subscriber.getClass();
    // 通过Class对象找到对应的订阅者方法
    List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    // 遍历订阅者方法集合,将订阅者和订阅方法订阅起来
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

   3.2  查找订阅者的订阅方法

List findSubscriberMethods(Class subscriberClass) {
    //  private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
    //  订阅者Class 对象为key , 订阅者中的订阅方法List 为value
    //  从缓存中查找该Event对应
    List subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    // ignoreGeneratedIndex 默认是false  常规我们通过getDefault获取EventBus 实例 不会修改该变量
    //  除非通过EventBus.Builder  设置
    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;
    }
}

      从上面的代码可以看出查找订阅方法是通过 findUsingInfo/findUsingReflection ,findUsingInfo其实是EventBus3.0 + 一个优化的亮点,我们知道之前EventBus版本获取订阅者方法是通过反射获取,而反射是运行时解析注解信息消耗性能。而现在一些主流的框架例如(ButterKnife等)开始在编译时解析注解信息(采用注解处理器),这样大大提升了性能。这两种方式从结果来看没有差异都是获取订阅者的订阅信息,只是对注解信息解析时机不同性能存在差异而已。EventBus3.0+ 提供了这一性能优化点。如果看到这篇博客想在这点优化的同学,可以参考官方文档http://greenrobot.org/eventbus/documentation/subscriber-index/。

    下面我们来看看findUsingInfo/findUsingReflection 两个方法的区别

private List findUsingInfo(Class subscriberClass) {
    // 获取FindState
    FindState findState = prepareFindState();
    // FindState 与 订阅Class绑定
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 获取订阅者信息
        // 通过SubscriberIndex 获取findState.clazz 对应的SubscribeInfo
        // 默认情况 getSubscriberInfo(findState) 返回为null  特殊情况  通过EventBus.Builder addIndex(SubscriberInfoIndex index)设置
        // SubscriberInfoIndex 是通过 SubscriberInfoIndex 就是一个接口,而注解生成器生成的类也是继承的它
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
private List findUsingReflection(Class subscriberClass) {
    // 创建并初始化FindState 对象
    FindState findState = prepareFindState();
    // findState 与 订阅Class关联
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
private void findUsingReflectionInSingleClass(FindState findState) {
    // getMethod 返回一个包含Method对象的数组,这些对象反应此Class对象所表示的类或接口的public方法
    // getDeclaredMethods 返回Method对象的一个数组,这些对象反映此Class对象的类或接口声明的所有方法,包括
    // 公共、保护、默认(包)访问所有方法,但是不包括继承
    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();
        // 方法修饰符判断  (public  && 非 static abstract)  下面的EventBusException 有说明
        // is a illegal @Subscribe method: must be public, non-static, and non-abstract
        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];
                    // 检测eventType 决定是否订阅,通常订阅者不能有多个eventType 相同的订阅方法
                    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)) {
                // strictMethodVerification EventBus.Builder 默认是false 开放了接口可以修改该变量
                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");
        }
    }
}

      通过上面的代码分析,我们可以看出当我们没有采用注解处理器生成注册索引类的时候其实findUsingReflection/findUsingReflectionInSingleClass 这两个方法是一样的都是执行findUsingReflectionInSingleClass,通过反射来获取订阅者订阅信息。获取完当前类的订阅者订阅信息信息后,再去父类去获取父类的订阅者订阅信息,将相应的订阅信息添加到容器(List)。

       是否添加到订阅信息容器时有个方法很重要findState.checkAdd(method, eventType),这个方法决定是否添加到容器当中,下面我们来看看这个方法的源码。    

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);
    }
}
private boolean checkAddWithMethodSignature(Method method, Class eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    String methodKey = methodKeyBuilder.toString();
    Class methodClass = method.getDeclaringClass();
    Class methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // Only add if not already found in a sub class
        return true;
    } else {
        // Revert the put, old class is further down the class hierarchy
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

     这个地方的代码是我看整个源码代码最痛苦的地方,其实他做得事情很简单就是如果子类订阅了这个事件那么父类订阅就失效,也就是不会添加的订阅事件的容器里。上面的注释很容易使人产生误解// Usually a subscriber doesn't have methods listening to the same event type,给人的感觉就是如果同一个类不同的方法订阅相同的事件就也不会添加到订阅事件容器中,而实际上同一个类不同方法订阅同一事件,是可以订阅成功的。之前看《重构》代码里面说到,当你的代码注释过多时,有两种可能第一你使用了很巧妙的算法,一般人很难理解你要详细的说明,第二种就是你的设计不友好只有非常详细的注释说明才能让阅读你代码的人理解。针对这个地方我更倾向于后者。如果我来设计这块我会遍历已经订阅的订阅者信息,看新的订阅事件容器中是否存在,如果存在看订阅者是否属于之前订阅者的子类,如果是则新的订阅信息不添加到订阅方法容器当中。

3.3 订阅者的注册

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // key:EventType value:Subscription   缓存订阅一个事件的不同订阅者
    CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
            // 当register被调用两次
            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;
        }
    }
    // key:Subscriber value: EventType  缓存一个订阅者订阅多个事件
    List> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (subscriberMethod.sticky) {
        //  eventInheritance 在EventBusBuild 中默认是true
        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).
            Set, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry, 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);
        }
    }
}

    我们先只关注非粘性事件,粘性事件我们接下来会单独讲解。上面的代码我们关注两个Map,

Map, CopyOnWriteArrayList> subscriptionsByEventType(key:EventType 订阅事件 value:CopyOnWriteArrayList 订阅者集合)
Map>> typesBySubscriber(key: Subscriber订阅者 valueList<Class>(订阅事件集合)  可以理解为订阅事件集)

  上面的两个Map非常重要,都和unRegister有关系,并且第一个Map和发送事件post是有关系的。讲到这里关于非粘性事件的register流程我们就讲完了。接下来我们来看看unRegister过程

  四、UnRegister

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    // 获取订阅者订阅的订阅事件
    List> 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());
    }
}

private void unsubscribeByEventType(Object subscriber, Class eventType) {
    List 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--;
            }
        }
    }
}

五、Post

/** Posts the given event to the event bus. */
public void post(Object event) {
    // 获取当前线程的posting状态 
    // private final ThreadLocal currentPostingThreadState = new ThreadLocal() 与当前线程绑定
    PostingThreadState postingState = currentPostingThreadState.get();
    // 获取当前事件队列
    List 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;
        }
    }
} 
  
final static class PostingThreadState {
    //事件队列 
    final List eventQueue = new ArrayList<>();
    //是否正在 posting 
    boolean isPosting;
    //是否为主线程 
    boolean isMainThread;
    // 订阅事件
    Subscription subscription;
    // 事件类型
    Object event;
    // 是否取消
    boolean canceled;
}
 
  

    综上所述:EventBus 用ThreadLoacl(这个在Handle源码也使用到作用与当前线程绑定)存储每个线程的PostingThreadState,当post一个事件的时候,添加到事件队列的末尾,等待前面的事件发布完成后再发布。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
    CopyOnWriteArrayList subscriptions;
    synchronized (this) {
        // 在register第二步订阅的时候我们提到这个Map,并且也说明这个map在post会使用到 
        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;
}
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);
    }
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        // 订阅线程线程跟随发布线程,EventBus默认的订阅方式
        case POSTING:
            // 订阅线程和发布线程相同,直接订阅
            invokeSubscriber(subscription, event);
            break;
        // 订阅线程在主线程
        case MAIN:
            // 如果发布线程在主线程直接订阅
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                // 如果不在UI线程,用mainThreadPoster进行调度,也就是HandlePoster的Handle异步处理,将订阅事件切换到主线程
                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:
            // 如果发布线程在UI线程,则将订阅事件添加到后台的线程的线程池
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
            // 不在UI线程,直接分发
                invokeSubscriber(subscription, event);
            }
            break;
        // 订阅线程为异步线程
        case ASYNC:
            // 使用线程池订阅
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

    订阅者指定了在哪个线程订阅事件,无论发布者在哪个线程,他都能将事件发布到订阅者指定的线程。我们通过发送线程的状态(是主线程还是子线程)以及订阅线程的状态,通过一定的方式来实现线程切换或者不用切换线程直接执行。下面会通过几个主要的Poster来看这种切换线程的处理方式。

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

5.1 HandlePoster

public class HandlerPoster extends Handler implements Poster {

    /**
     *  存放执行的PostEvents 的事件队列
     */
    private final PendingPostQueue queue;

    /**
     *   Post 事件在handleMessage 中执行的最大时间值,超出这个时间会抛出异常
     */
    private final int maxMillisInsideHandleMessage;

    private final EventBus eventBus;

    /**
     *  标识Handle 是否被运行起来
     */
    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;
        }
    }
}

内部实现一个Handle、Poster(enqueue方法),执行enqueue方法时实际上是通过Handle sendMessage。在通过在主线程中handleMessage 执行 eventBus.invokeSubscriber(pendingPost),实现子线程到UI线程的切换执行订阅。

5.2 BackgroundPoster

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

}

     内部实现Runnable、Poster(enqueue),enqueue方法实质是通过线程池执行runnable方法达到UI线程到子线程的切换。

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

}

    AsyncPoster 和 BackgroundPoster 原理一样这样就不再多说了。

6、StickEvent 粘性事件

    我们一般订阅普通事件的时候,从A到B,如果A作为观察者监听B的状态,当B发生改变的时候,A需要被通知到发生相应的改变。这种场景我们会经常遇到例如(个人信息界面,点击条目跳转到相应的界面修改相应的信息修改后,回到个人信息界面相应的显示信息也发生改变),这样的场景我们使用简单普通事件则可以完成。试想如此存在这样一个场景,还是A到B ,A是被观察者,B是观察者,A发生变化后B 要知道,那么这样的场景显然普通的Event事件不能做到,这时候StickEvent事件排上用场。可能这这里读者有疑问,为什么这种场景StickEvent就可以做到。它的实现机理是如何的,其实StickEvent 实现机理和普通Event事件有点相反。相反如何解释,正常的事件时先进行事件的注册订阅将相应的订阅事件添加到容器中,post的时候触发事件的进行,而StickEvent不是这样的,postStick的时候他是将粘性事件添加到stickEvent 容器当中,当register注册的时候,实际是是他执行订阅方法的时候。

     具体原理详见如下源码:

private static final Map, List>> eventTypesCache = new HashMap<>();
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);
}
if (subscriberMethod.sticky) {
    //  eventInheritance 在EventBusBuild 中默认是true
    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).
        Set, Object>> entries = stickyEvents.entrySet();
        for (Map.Entry, 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);
    }
}
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    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, isMainThread());
    }
}

    普通事件的移除是通过unregister 而StickEvent 是通过 EventBus 中 removeStickyEvent方法进行移除,当然有相应api获取相应的StickEvent 获取到相应事件后可以修改部分信息这点类似于粘性广播。

    写到这里我的EventBus3.0+写完了,如果读者看到这里可以回到我之前引入的问题,那说明读者已经看懂了这篇博客,也理解了EventBus3.0+源码。如果还不能很好理解,那读者可能需要再回头阅读更好的方式是下载源码对比阅读。

7、闪光点

     7.1 EventBusBuild 的设计

     EventBus参数的配置项实际上是通过EventBusBuild 配置的,通过我们调用getDefault方式时间是是通过一个默认的EventBusBuilld 配置项获取EventBus 实例。这样设计的好处便于拓展,通过EventBusBuild你可以拓展自己的其他配置,而针对普通用户不需要拓展的通过一个默认配置项配置获取。这种设计方式可以应用到我们设计组件的时候,如果我们对外暴露的接口可以传入很多配置项的,这时候我们可以考虑这种设计,一般用户我们采用一个默认的配置。

    7.2 对象池的使用

    我们知道EventBus register/unregister 时会频繁创建对象,而短时间内频繁创建对象可能造成内存抖动,针对这样频繁创建对象的场景可以考虑使用对象池。

    7.3 切换线程策略模式的使用

     线程切换的时候抽离一个Poster接口,UI线程切换子线程实际上执行线程池excute runnable方法,而子线程切换UI线程执行Handler sendMessage().

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(EventBus3.0及以上源码解析)