Source code analysis-understanding the EventBus workflow

EventBus source code analysis

EventBus is an open source library that we use quite frequently. It is relatively simple to use and powerful. How can we not explore its internal implementation for such a powerful library?
##EventBus's creation and variables
###Create EventBus object

When we use EventBus, we often use EventBus.getDefault(), use the static method getDefault() for an object, and then perform operations such as register and post. The construction method of the EventBus class has only a non-parameter construction externally, and the EventBus (EventBusBuilder builder) method is called internally. There are actually four ways to obtain EventBus objects:

  • **EventBus.getDefault():** The most commonly used method, singleton mode, uses the default configuration. All calls in this method are used in the app to ensure that the obtained object is unique, and it will not cause the subscription event and the sending event to be sent without the same EventBus object.
  • new EventBus(): Less used, and the default configuration is also used
  • EventBus.builder().build(): Build an object through the Builder method, which can be understood as a builder mode and can be manually configured (log printing, sending exception events, throwing exceptions, etc.)
  • **EventBus.builder().installDefaultEventBus(): **It is also an object built in the way of Builder, which can be configured manually. The difference from build() is that it builds the default object of EventBus, which is the same as EventBus.getDefault( ) Get the same object. Note: The installDefaultEventBus() method needs to be called before EventBus.getDefault(), otherwise the object has been created, the configuration is invalid, and an exception is thrown.

EventBus member variables

Introduce several key variables:

  • The default EventBuilder object, the default configuration
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
  • Key variable, a collection of all subscription event objects (including subscribers and subscription methods) distinguished by subscription event types, and all methods of subscribing to a certain event can be found
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  • The key variable, the subscriber and its corresponding collection of all subscribed events, can find out all the events subscribed by the subscriber according to the subscriber
private final Map<Object, List<Class<?>>> typesBySubscriber
  • A more important variable, the PostingThreadState class describes the state of the current post. All events sent through the ost method will be stored in the queue in this class first, and then taken out of the queue for distribution operations . In addition, this class contains variables such as whether it is being distributed, whether the post operation is in the main thread, and whether the operation is cancelled.
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
  • Separately control the distribution in the main thread, background thread distribution, and asynchronous thread distribution operations
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
  • Find all subscription methods in a certain subscriber through this object
private final SubscriberMethodFinder subscriberMethodFinder;

EventBus subscription

Register()

Subscription, that is, the register() method, we know that after calling EventBus.getDefault().register(this) in a class, the registered subscription event can be received in that class.

Before that, we first understand two classes:

**SubscriberMethod: **Subscriber method class, which is the method that we use @Subscribe annotated in our code to monitor and receive methods for processing events.

final Method method;         //方法名、参数等信息,android原生反射类
final ThreadMode threadMode; //在哪一个线程中接收此事件
final Class<?> eventType;    //事件类型,如String.class等
final int priority;          //优先级,
final boolean sticky;        //是否粘性传输
String methodString;         //用来检测是否是同一个监听方法

**Subscription:** the object corresponding to the subscriber and an event method in it

final Object subscriber;                //订阅者(例如某一个Activity对象)
final SubscriberMethod subscriberMethod;//订阅者内的一个订阅方法

Continue the register process:

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //找出这个类监听的所有事件,遍历列表进行订阅相关操作
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

It can be seen that there are not many things written in this method. The first is to find out all subscription methods through the aforementioned subscriberMethodFinder. The specific implementation is to find out through reflection. Then register and bind these methods respectively, so continue to look at the subscribe() method.

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
	    //获取该订阅方法的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        //创建一个新的订阅者和订阅方法组合对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //根据事件类型,获取当前所有监听该类型的订阅操作(订阅者和订阅方法)
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //如果找不到,新建列表,以当前事件类型为key塞入map集合中
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //同一个订阅者对象中,如果有该订阅方法,不能重复订阅,也就是一个类中的onEvent方法不能是相同的参数(事件类型)
            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) {
            ...
        }
    }

This method is the key to the entire register process, but in simple terms, it can be summarized as two points:
1. Obtain a list of all registered event types, insert the newly found method into the list, and finally use the event type as the key and the list as The value is updated or inserted into the map collection.
2. Get a list of all related subscription operations for this subscriber, with the subscriber as the key and the list as the value, and stuff them into the map collection.

unRegister()

Generally, the registration is canceled in the onDestroy method, and the receiving message is no longer monitored after canceling the registration. From the register process, we can see that in unRegister, we only need to reverse it. How to reverse it? Remove, remove, remove!

    public synchronized void unregister(Object subscriber) {
        //找出这个订阅者订阅的所有事件
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
	        //遍历这些事件方法,在事件类型-订阅操作map集合中找出这些方法并移除
            for (Class<?> eventType : subscribedTypes) {
	            //方法实现为:找出这个事件类型的所有订阅操作,循环匹配如果是当前这个订阅者订阅的,从列表中移除
                unsubscribeByEventType(subscriber, eventType);
            }
            //最后从map集合中移除这个订阅者
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

##Event monitoring

Subscribe comment

When using the @Subscribe annotation, sometimes a code like threadMode = ThreadMode.MAIN is added. Why is this used? Set up the thread where the listener receives the event!

Not only that, we can also set whether the event is sticky or not, and set the priority. Subscribe notes:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
	//可设置线程模式,默认为POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
	//是否粘性传输,默认false
    boolean sticky() default false;
	//优先级,默认为0,排到队尾
    int priority() default 0;
}

ThreadMode

This is a fairly impressive setting in EventBus, which allows you to specify in which thread to execute the operation.

####ThreadMode.POSTING
default mode. The event will be directly distributed to the subscribers after the post, and will be executed in the same thread as the sending event . Because thread switching is avoided, the overhead is minimal . This is also the officially recommended mode, if your project does not involve too many switching between threads. But try not to perform time-consuming operations in the method to avoid blocking when the sending thread is the main thread .

####ThreadMode.MAIN
event monitoring and execution will be carried out in the main (UI) thread . If the sending event is in the main thread, it will be executed directly, otherwise, the main thread will be queued and distributed for execution through the poster. Also do not perform time-consuming operations in the method to avoid blocking the main thread .
####ThreadMode.MAIN_ORDERED is
basically the same as MAIN. The difference is that no matter what thread the event is sent from, it must be queued and executed uniformly through the main thread Poster. ,
####ThreadMode.BACKGROUND
background thread execution. If the sending event is not in the main thread, it will be executed directly in the current thread; if the sending event is in the main thread, EventBus will start a unique background thread, and all events will be queued for execution in this thread. It is also best not to perform time-consuming operations in methods.
####ThreadMode.ASYNC
is executed in asynchronous thread. Regardless of the thread that sends the event, it will not be executed in this thread. A new asynchronous thread is opened to perform operations. This mode is generally used when you need to perform time-consuming operations in the monitor. EventBus will use the thread pool to manage to avoid opening too many threads.

Send event

This is more critical. You only need to post a simple post when you use it. Then how do subscribers receive these events? And still want to be in which thread? Similarly, let's first understand a few categories.

The PendingPost
class is used to describe an event to be sent, and a queue is maintained internally for reuse.
Get an object, reuse the list:

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        /**
         * 如果当前正在从队列中复用,则直接return一个new出来的对象
         * 如果队列为空,同样返回新对象
         * 如果能够复用,则取队列最后一个对象,将其变量置为传入的参数,最后return这个对象
         */
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                //取对象使用的是remove方法,从队列中移出,这样保证队列不会越来越长
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

Release the object after use:

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            /**
             * 当前队列不能超过10000,因为每一次obtain要么获取一个新对象,要么从队列中移除最后一个对象,
             * 所以即是保证当前同一个线程同一时间内需要发送的事件操作不超过10000
             */
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

In other words, every post we send an event, we will eventually create such an object ready to send, which contains the sent event object, event type, subscriber information, subscription method and other information.

The PendingPostQueue
class can be regarded as a linked list, and each node is a PendingPost object, which is connected by the next variable of PendingPost. The value and removal can refer to the usage of the single linked list.

###Start Post
1. Actively call the post() method to send an event, pass in an Object object
2. Get the PostingThreadState object we mentioned at the beginning, add the event to the list inside, and judge the current state.

    public void post(Object event) {
        /**
         * 获取当前发送对象,添加到队列中,如果正在发送其他事件,则在发送后会继续发送新加入的事件(while循环)
         * 如果当前空闲,则立即发送事件
         * 最后重置状态
         */
        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 {
                //从队列中获取事件对象使用remove方法获取,省去发送后从队列删除这一操作
                //列表为空时退出循环,因为前面的逻辑,是有可能正在post操作的时候,新的post发过来了,添加到了列表中,
                //但是还没有没有真正发送,所以发送完一个之后需要判断是否还有需要发送的事件
                while (!eventQueue.isEmpty()) {
		            //真正发送在这里
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

3. According to the event type, find in the collection whether there is a subscription method that subscribes to this event type, if not found, throw an exception; if found, process each subscription operation in a loop

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

5. In the final sending, select the corresponding poster distribution according to the thread selected by the subscription method

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //当初注册@Subscribe注解时设置的在哪个线程中接收事件,默认Posting,即当前线程(和发送者同一线程)
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
	            //直接当前线程执行
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
	            //如果当前是主线程,直接执行
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                //否则使用主线程Poster统一排队分发
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
	            //不管处于何线程,统一使用主线程Poster排队分发
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
	            //如果不在主线程,直接执行,否则,使用后台Poster排队分发
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
	            //不管处于何线程,统一使用异步Poster排队分发
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

carried out

Implementation is relatively simple, using java reflection mechanism

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

A few posters

In which thread the event is received depends on which Poster is used to send it. The default is to execute it directly. The main thread executes using HandlerPoster, background execution uses BackgroundPoster, and asynchronous execution uses AsyncPoster.
First look at Poster, an excuse class for public implementation:

interface Poster {
    void enqueue(Subscription subscription, Object event);
}

There is an execution method, which is to send an event

AsyncPoster

Asynchronous Poster implements the Runnable and Poster interfaces, and uses Runnable to open a new thread for execution.
When the enqueue method is executed, the run() method that must be implemented in Runnable is executed internally. This is a new thread for execution, and the invokeSubscriber() reflection method in EventBus is called in the thread.

The
difference between BackgroundPoster and AsyncPoster is that it only opens one thread. When an event comes in and needs to be distributed, it first determines whether other operations are currently being performed. If it is free, execute it immediately, otherwise insert the list and queue first, and wait until the previous event is processed. Then issue the next event.

HandlerPoster
is the main thread Poster at the end. When this Poster is created, Looper.getMainLooper() is passed in , so it realizes monitoring on the main thread. This Poster uses the Handler mode. When it receives a request to send a message, it calls its own sendMessage() method, and then processes the request in handleMessage.

    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            //开始处理时间
            long started = SystemClock.uptimeMillis();
            while (true) {
	            //从队列中取出一个PendingPost操作
                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;
                //如果循环执行时间大于10毫秒,退出循环,避免一直从队列中取数据,
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

End

At this point, the entire EventBus process is basically over. This article starts from the analysis of the process, and basically explains the entire EventBus framework and internal implementation principles. There are many shortcomings in this. Please feel free to enlighten me.
thank!

Guess you like

Origin blog.csdn.net/lizebin_bin/article/details/80409169