EventBus 流程解析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/u013174702/article/details/85619490

在这里插入图片描述
先介绍控件使用方法,然后再从基本的使用方法断点调试,整体了解一下流程。

EventBus 基本使用

在 module 的 build.gradle添加

implementation 'org.greenrobot:eventbus:3.1.1'

在接收消息的地方注册 eventBus

EventBus.getDefault().register(this);

然后创建一个事件类

public class FirstEvent {
    public String message;

    public FirstEvent(String message){
        this.message = message;
    }
}

在注册 EventBus 的类中创建接收事件方法

// 这里的threadMode可以设置不同的值,在不同的线程中处理事件接收
@Subscribe(threadMode = ThreadMode.MAIN)
public void setText(FirstEvent event){
    textView.setText(event.message);
}

然后在任意一个地方发送一个消息

EventBus.getDefault().post(new FirstEvent("来自第某个地方的消息"));

EventBus一个基本的流程就这样完成了,接下来看每一步的源码

注册流程

EventBus.getDefault()

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

这是一个单例方法,用的是 DoubleCheck 的方式,创建了一个 EventBus 的实例。

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 方法是将注册的类与该类中事件处理的方法进行一个绑定。

SubscriberMethod 类是一个实体类,将订阅相关的数据进行一个封装:
image.png

然后我们看一下 subscriberMethodFinder.findSubscriberMethods() 方法

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

findSubscriberMethods() 方法会先从 METHOD_CACHE 中查找,没有找到则会根据 ignoreGeneratedIndex 标志选择是采用反射的方式寻找还是用 findUsingInfo 方法,这里这个标志和 findUsingInfo 方法目前都不知道是什么。找到后会将结果加入到缓存中。

接下来就是 subscribe() 方法

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

	// 后面是粘性事件相关处理,先不管
    ...
}

subscribe 方法主要做两件事,一个是 subscriptionsByEventType.put(eventType, subscriptions) ,subscriptionsByEventType 是以事件的类为 key,订阅者的回调方法为 value 的映射关系表。

另一个是 typesBySubscriber.put(subscriber, subscribedEvents) , typesBySubscriber 是以订阅者为 key, 订阅者订阅的事件类为 value 的映射关系表。

到这里一个 EventBus 的注册流程就结束了,从这个流程来看,主要就是建立事件类和订阅者之间的映射关系。

注销流程

unregister()

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

注销流程就很简单了,先取出该订阅者对应的订阅事件类,然后遍历订阅的事件类,再取出该类所有的订阅者,把我们注销的订阅者移除,最后再把注销的这个订阅者从 typesBySubscriber 中移除。

Post 流程

先看下post的流程图

image.png

然后解析每个步骤,看下都做了哪些操作

post ()

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

PostingThreadState 的实例存储在 ThreadLocal 中,是一个内部静态类,将事件队列,post 状态,事件和订阅者等信息封装在一起。post 方法取出当前线程的PostingThreadState,将事件添加到事件队列中,设置一些状态信息,然后循环把事件队列(用一个 ArrayList 实现)的事件取出来分发。

postSingleEvent()

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    // 标志位,决定是否要post事件所有父类和实现接口
    if (eventInheritance) {
    	/* lookupAllEventTypes 方法是找到该事件的所有父类和所有实现的接口*/
        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) {
        ...
        // 事件没有订阅者就打打日志啥的
    }
}

postSingleEvent 方法就两个逻辑,一个是决定是否要post事件所有父类和实现接口,另一个就是事件没有订阅者,会判断一些条件,然后重新 post 一个 NoSubscriberEvent 事件。

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

没啥多说的,根据传过来的事件,拿到对应的订阅关系,PostingThreadState 中记录信息,然后交给 postToSubscription 方法。

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

最后根据订阅者的回调方法设置的线程,决定再什么线程上反射调用该回调方法,反射调用通过 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);
    }
}

这里我们重点关注 EventBus 是如何让事件回调在不同线程中执行的:

  • POSTTING: 不用多说,再哪个线程抛出来的事件,就再哪个线程直接反射调用回调方法,不涉及线程切换
  • MAIN: 在主线程中反射调用回调事件,如果当前线程不是主线程,则通过 mainThreadPoster 插入一个事件, mainThreadPoster 是通过主线程 Looper 创建的一个Handler,调用 enqueue() 方法,会通过自身发一个消息,然后在自己的 handleMessage() 方法中反射调用事件的回调方法,这样就完成了在主线程回调。
  • MAIN_ORDERED: 也是在主线程中反射调用回调方法,但是有顺序,不会发生第二个事件早于第一个事件执行完?不太确定。
  • BACKGROUND: 如果当前线程是主线程,则将事件添加到 backgroundPoster 中,backgroundPoster的 enqueue() 方法会通过线程池执行一个任务,反射调用回调方法。
  • ASYNC: 无论在哪个线程,都交给线程池去处理这个事件。

到这,Post 流程分析基本完成。

猜你喜欢

转载自blog.csdn.net/u013174702/article/details/85619490