EventBus工作流程及原理

EventBus工作流程及原理

几种线程模式(ThreadMode)

  1. POSTING
    该模式是指事件的发送和处理均处于同一线程,且为默认模式
  2. MAIN
    该模式下,事件的消费在主线程
  3. MAIN_ORDERED
    事件的消费有序在主线程中发生
  4. BACKGROUND
    a. 事件的发送在子线程,事件的消费也在当前子线程中
    b. 事件的发送在主线程,会新建一个子线程来消费该事件(订阅的方法在子线程中执行)
  5. ASYNC
    事件的消费在独立的子线程中,既不是事件发送的线程,也不是主线程

使用

要理解EventBus是如何运行的,就得先知道其是如何使用的

注册

EventBus.getDefault().register(this)

发送事件

EventBus.getDefault().post(new Event())

这里的事件是自定义的,可根据实际的数据需求创建不同的构造函数从而实现数据的传递

事件的消费

@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(Event event) {
   //处理逻辑
}

注意这里事件的处理方法必须加上Subscribe注解,否则无法找到,具体的原因后续进行分析的时候再进行说明

原理

实例的创建

首先,EventBus在创建实例的时候采用了双锁单例的模式来进行创建,保证了线程的安全性

public static EventBus getDefault() {
    EventBus instance = defaultInstance;
    if (instance == null) {
        synchronized (EventBus.class) {
            instance = EventBus.defaultInstance;
            if (instance == null) {
                instance = EventBus.defaultInstance = new EventBus();
            }
        }
    }
    return instance;
}

注册

EventBus在进行注册的时候到底干了什么事情勒,源码如下:

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

注册的时候传入了this作为参数,也就是注册所在的类作为subscriber,首先,会获取到注册者的class,随后根据该class类型,查找该宿主的哪些方法订阅了事件进行消费,具体的查找方式为

List findSubscriberMethods(Class subscriberClass) {
    List 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;
}

}

这里存在两种查找的方式,主要是EventBus这里进行了优化,首先看第一种,从名字就可以看出是通过反射的方式进行查找

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

这里值得一提的是FindState的创建,调用prepareFindState()方法,采用享元设计模式增加缓存来减少对象的创建,因为可能存在多个方法进行消费,所以这里也是为了节约内存

private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

这里的FIND_STATE_POOL是一个数组,最大为4,继续上面通过反射对订阅方法进行查找方法消费,最终是调用到findUsingReflectionInSingleClass(FindState findState)方法,看看具体做了啥

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
        try {
            methods = findState.clazz.getMethods();
        } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
            String msg = "Could not inspect methods of " + findState.clazz.getName();
            if (ignoreGeneratedIndex) {
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        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");
        }
    }
}

可以看到,这里通过反射的方式拿到了订阅者的所有方法,并找到带有Subscribe注解的方法,拿到所有的其所订阅的事件类型和线程模式,并创建SubscriberMethod最终保存在findState.subscriberMethods中进行返回,可以继续看到findUsingReflection方法中的这段逻辑

while (findState.clazz != null) {
    findUsingReflectionInSingleClass(findState);
    findState.moveToSuperclass();
}

就是这里不仅会找传入的订阅者注册的一些方法,还会找出其父类订阅的一些方法进行返回,最终,找到的订阅方法会以subscriberClass, subscriberMethods键值对的形式放入METHOD_CACHE中,保证了线程的安全

private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();

第二种说的查找的方法就是调用findUsingInfo方法,这里的info便是SubscriberInfo或者是subscriberInfoIndexes进行查找,可以看到SubscriberInfo为一个接口类型,也就是需要我们自己去实现,然后实现getSubscriberMethods()方法,也就是我们将订阅相关的信息,告诉EventBus,然后EventBus通过该信息直接找到订阅者相关的方法

同样,通过该方式找到后的subscriberMethods也存放在METHOD_CACHE中进行保存,上述只是找到了订阅者消费事件的方法,接下来则是进行订阅

synchronized (this) {
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod);
    }
}

这是register方法中的一段代码,就是订阅的逻辑,这里对每个找到的方法进行订阅,接下来看看是如何进行订阅的

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList 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> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

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

可以看到,这里首先获取了消费事件的方法所消费的事件类型eventType,每一个Subscription是一个订阅者-订阅方法的实体类,subscriptionsByEventType则是一个Map,其是以
eventType - CopyOnWriteArrayList为键值对的数据格式,这里分析到,这里的subscriptionsByEventType以事件类型作为key,保存了订阅该事件的订阅者以及消费该事件方法相关的信息。接下来看订阅过程中的一段代码

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

结合上面完整的代码看,subscriptions为一个事件类型对应的订阅者相关的信息,newSubscription为一个新的订阅者相关的信息,这里便是对一个新的订阅者信息处理的过程,这里巧妙的i== size判断了两种情况:

  1. subscriptions为空的时候,直接将newSubscription插入第一个
  2. 当newSubscription的订阅方法的优先级最低时,则插到最后

这里对newSubscription进行了一个饿优先级的排序,继续看后面的处理

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

typesBySubscriber是一个订阅者- 事件类型列表的键值对的Map,也就是说这里维护了一个订阅者所订阅的所有事件类型的Map,供后续进行使用
到这里,注册便已完成,接下来是事件的发送以及相关的消费

事件发送

通过上面的使用可知,事件的发送是通过EventBus的post方法,那么是如何实现的呢?还是看下源码就知道啦

public void post(Object event) {
    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;
        }
    }
}
 
  

这里首先通过ThreadLocal拿到当前线程的posting状态,其包含了事件队列,以及订阅者相关的信息,这里用ThreadLocal也保证了一个线程只有一个事件队列等状态信息

final static class PostingThreadState {
    final List eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}    
 
  

首先将要发送的事件添加到该线程事件队列中去,并通过轮询的操作将队列中的事件进行post,接下来看看postSingleEvent做了什么事

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List> 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进行控制的,具体多实现的步骤就是在lookupAllEventTypes方法中

private static List> lookupAllEventTypes(Class eventClass) {
    synchronized (eventTypesCache) {
        List> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

这里拿到了该事件类型的所有超类或者间接超类的事件类型,存放在list中,随后便通过遍历拿到事件进行post,而没有eventInheritance时则是直接将该事件进行了post,具体的实现在

postSingleEventForEventType中
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
    CopyOnWriteArrayList subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            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;
}

这里首先通过事件的类型拿到所有的订阅者,如何拿到的呢?就是subscriptionsByEventType,前面我们所说的以事件类型 - 订阅者信息为键值对的map,这个map存贮了这样的对应关系,随后便是熟悉的轮询将事件post到对应的订阅者
这里有个小插曲,我们看到这里有个abort,能将当前的事件的post给停掉,我记得当时面试的时候有个面试官面试过我一个关于EventBus的源码问题,说当我post一个事件,订阅者有很多,我只想让那个A、B两个收到我的事件,该如何做,我当时还傻傻的想这如何实现,类似广播截断,其实现在想,面试官其实还是想考我源码,这里的abort其实就能实现,具体的做法就是:记得之前在保存订阅者信息的时候有个subscriptions,在添加newSubscription的时候,会根据优先级进行排序,因此这里配合优先级以及Abort,实现的方法就来了;将A、B的消费事件的方法优先级设置为较高,放在队首,当A、B中的优先级较低的一个处理完事件后,调用EventBus的cancelEventDelivery方法,就能将该事件的传递终止;所以其实还是考的对源码的理解深度

事件消费

事件的最终消费是通过,其消费的方式也是通过我们消费方法限定的线程模式来进行区分的,具体的线程模式在文章前就进行了描述,这里以MAIN线程模式的进行分析,其他的也就是大同小异了

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;
        ...
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

这里首先通过isMainThread来进行判断当前的post的线程是否是主线程,由于事件消费的规定在主线程,因此这里可以直接调用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);
    }
}

如果post线程不是主线程,那么肯定得进行线程的切换,mainThreadPoster.enqueue(subscription, event)就是相应的实现,这里的mainThreadPoster是在创建EventBus的时候创建的
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

具体的实现类就是AndroidHandlerMainThreadSupport,创建的Poster其实就是HandlerPoster,其是Handler的子类,所以这里的mainThreadPoster.enqueue其实就是可以看作是Handler.sendMessage的作用,只不过这里包装了一层,在enqueue的时候,将事件和订阅者封装成PendingPost,放进PendingPostQueue队列中,随后便调用sendMessage方法,并在handleMessage中进行处理,通过从PendingPostQueue队列取出PendingPost,随后调用EventBus的invokeSubscriber(PendingPost pendingPost)重载方法,并最终还是调用invokeSubscriber(subscription, event)进行反射后调用订阅者的消费方法进行消费
整个流程就走通了

边边角角

Sticky事件

加入存在这样的一种情况,当事件发送的时候,订阅者还未进行注册,此时订阅者该如何接受到该事件呢?sticky帮你解决这个问题

public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    boolean sticky() default false;
    int priority() default 0;
}

在Subscribe注解中还包含了一个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);
}

通过postSticky的方式,将事件post,由于此时订阅者还未注册,因此,这里就没那么多麻烦事了,先是将该粘性事件保存在stickyEvents这个map中,随后直接post事件,其流程和普通流程的post一致,会一直走到postToSubscription随后调用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);
    }
}

但是这时候有个问题,这里没有订阅者,因此该invloke方法就会调用失败,从而会catch住,调用handleSubscriberException方法

private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
    if (event instanceof SubscriberExceptionEvent) {
        if (logSubscriberExceptions) {
            // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
            logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
                    + " threw an exception", cause);
            SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
            logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in "
                    + exEvent.causingSubscriber, exEvent.throwable);
        }
    } else {
        if (throwSubscriberException) {
            throw new EventBusException("Invoking subscriber failed", cause);
        }
        if (logSubscriberExceptions) {
            logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
                    + subscription.subscriber.getClass(), cause);
        }
        if (sendSubscriberExceptionEvent) {
            SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
                    subscription.subscriber);
            post(exEvent);
        }
    }
}

这里会创建一个SubscriberExceptionEvent进行post随后进行打印,这里是EventBus的异常Case的情况,真正sticky实现还是在register方法里面,在post的时候,我们保存了该粘性事件,而在register的时候

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

如果订阅者订阅的事件是粘性事件,则会从stickyEvents去拿,如果存在则直接调用postToSubscription方法让订阅者消费该事件,来完成在注册的第一时间就让订阅粘性事件的订阅者消费改事件

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

总结

EventBus的大致原理模型图如下:
EventBus工作流程及原理_第1张图片

你可能感兴趣的:(安卓学习,android)