Анализ исходного кода - понимание рабочего процесса EventBus

Анализ исходного кода EventBus

EventBus - это библиотека с открытым исходным кодом, которую мы используем довольно часто. Она относительно проста в использовании и мощна. Как мы можем не исследовать ее внутреннюю реализацию для такой мощной библиотеки?
## Создание EventBus и переменные
### Создание объекта EventBus

Когда мы используем EventBus, мы часто используем EventBus.getDefault (), используем статический метод getDefault () для объекта, а затем выполняем такие операции, как регистрация и публикация. Метод построения класса EventBus имеет только внешнюю конструкцию без параметров, а метод EventBus (построитель EventBusBuilder) вызывается изнутри. На самом деле есть четыре способа получить объекты EventBus:

  • ** EventBus.getDefault (): ** Наиболее часто используемый метод, одноэлементный режим, использует конфигурацию по умолчанию. Все вызовы этого метода используются в приложении, чтобы гарантировать, что полученный объект уникален, и это не приведет к отправке события подписки и отправляющего события без одного и того же объекта EventBus.
  • new EventBus (): используется реже, также используется конфигурация по умолчанию
  • EventBus.builder (). Build (): создайте объект с помощью метода Builder, который можно понимать как режим построителя и который можно настроить вручную (печать журнала, отправка событий исключения, выдача исключений и т. Д.)
  • ** EventBus.builder (). InstallDefaultEventBus (): ** Это также объект, созданный способом Builder, который можно настроить вручную. Отличие от build () заключается в том, что он создает объект EventBus по умолчанию, который совпадает с EventBus.getDefault ( ) Получите такой же объект. Примечание. Метод installDefaultEventBus () необходимо вызвать перед EventBus.getDefault (), в противном случае объект был создан, конфигурация недействительна и возникает исключение.

Переменные-члены EventBus

Введите несколько ключевых переменных:

  • Объект EventBuilder по умолчанию, конфигурация по умолчанию
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
  • Ключевая переменная, набор всех объектов событий подписки (включая подписчиков и методы подписки), различающихся типами событий подписки, и все методы подписки на определенное событие.
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  • Ключевая переменная, подписчик и соответствующая ей коллекция всех подписанных событий, может узнать все события, на которые подписан подписчик, в соответствии с подписчиком.
private final Map<Object, List<Class<?>>> typesBySubscriber
  • Более важная переменная, класс PostingThreadState, описывает состояние текущего сообщения. Все события, отправленные через метод ost, будут сначала сохраняться в очереди в этом классе, а затем удаляться из очереди для операций распределения . Кроме того, этот класс содержит такие переменные, как то, распространяется ли он, находится ли пост-операция в основном потоке и отменена ли операция.
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
  • Отдельно контролировать распределение в основном потоке, распределение фоновых потоков и операции асинхронного распределения потоков.
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
  • Найти все методы подписки у определенного подписчика через этот объект
private final SubscriberMethodFinder subscriberMethodFinder;

Подписка на EventBus

Регистр()

Подписка, то есть метод register (), мы знаем, что после вызова EventBus.getDefault (). Register (this) в классе, зарегистрированное событие подписки может быть получено в этом классе.

Перед этим мы сначала понимаем два класса:

** SubscriberMethod: ** Класс метода подписчика, который является методом, который мы используем @Subscribe, аннотированный в нашем коде, для отслеживания и получения методов обработки событий.

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

** Подписка: ** объект, соответствующий подписчику и метод события в нем.

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

Продолжите процесс регистрации:

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

Видно, что в этом методе написано не так много вещей. Во-первых, необходимо выяснить все методы подписки с помощью вышеупомянутого subscriberMethodFinder. Конкретная реализация заключается в том, чтобы узнать через отражение. Затем зарегистрируйте и свяжите эти методы соответственно, так что продолжайте смотреть на метод subscribe ().

    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) {
            ...
        }
    }

Этот метод является ключом ко всему процессу регистрации, но, проще говоря, его можно резюмировать как два пункта:
1. Получите список всех зарегистрированных типов событий, вставьте вновь найденный метод в список и, наконец, используйте тип события в качестве ключа и список как Значение обновляется или вставляется в коллекцию карт.
2. Получите список всех связанных операций подписки для этого подписчика, с подписчиком в качестве ключа и списком в качестве значения, и вставьте их в коллекцию карт.

unRegister ()

Как правило, регистрация отменяется в методе onDestroy, и получающее сообщение больше не отслеживается после отмены регистрации. Из процесса регистрации мы видим, что в unRegister нам нужно только отменить его. Как отменить его? Удалите, удалите, удалите!

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

## Мониторинг событий

Подпишитесь на комментарий

При использовании аннотации @Subscribe иногда добавляется код типа threadMode = ThreadMode.MAIN. Почему он используется? Настройте поток, в котором слушатель получает событие!

Мало того, мы также можем установить, является ли событие закрепленным или нет, и установить приоритет. Примечания к подписке:

@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

Это довольно впечатляющий параметр в EventBus, который позволяет вам указать, в каком потоке выполнять операцию.

####
Режим ThreadMode.POSTING по умолчанию. Событие будет напрямую распространено среди подписчиков после публикации и будет выполняться в том же потоке, что и отправляющее событие . Поскольку переключение потоков исключено, накладные расходы минимальны . Это также официально рекомендуемый режим, если ваш проект не требует слишком большого количества переключений между потоками. Но старайтесь не выполнять в методе трудоемких операций, чтобы избежать блокировки, когда отправляющий поток является основным потоком .

#### Мониторинг и выполнение
событий ThreadMode.MAIN будет осуществляться в основном (UI) потоке . Если событие отправки находится в основном потоке, оно будет выполняться напрямую, в противном случае основной поток будет поставлен в очередь и распределен для выполнения через плакат. Также не выполняйте трудоемкие операции в методе, чтобы избежать блокировки основного потока .
#### ThreadMode.MAIN_ORDERED в
основном то же самое, что и MAIN. Разница в том, что независимо от того, из какого потока отправлено событие, оно должно быть поставлено в очередь и выполняться равномерно через основной поток Poster. ,
#### ThreadMode.BACKGROUND выполнение
фонового потока. Если событие отправки не находится в основном потоке, оно будет выполнено непосредственно в текущем потоке; если событие отправки находится в основном потоке, EventBus запустит уникальный фоновый поток, и все события будут поставлены в очередь для выполнения в этом потоке. Также лучше не выполнять в методах трудоемких операций.
#### ThreadMode.ASYNC
выполняется в асинхронном потоке. Независимо от потока, отправляющего событие, оно не будет выполняться в этом потоке. Для выполнения операций открывается новый асинхронный поток. Этот режим обычно используется, когда вам необходимо выполнить длительные операции в мониторе. EventBus будет использовать пул потоков, чтобы избежать открытия слишком большого количества потоков.

Отправить мероприятие

Это более важно. Вам нужно только опубликовать простой пост, когда вы его используете. Тогда как подписчики получают эти события? И все же хотите быть в какой ветке? Точно так же давайте сначала разберемся с несколькими категориями.

Класс PendingPost
используется для описания отправляемого события, а внутренняя очередь поддерживается для повторного использования.
Получите объект, повторно используйте список:

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

Освободите объект после использования:

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

Другими словами, каждый пост, который мы отправляем, мы в конечном итоге создаем такой объект, готовый к отправке, который содержит отправленный объект события, тип события, информацию о подписчике, метод подписки и другую информацию.

Класс PendingPostQueue
можно рассматривать как связанный список, а каждый узел является объектом PendingPost, который связан следующей переменной PendingPost.Значение и удаление могут относиться к использованию единого связанного списка.

### Start Post
1. Активно вызовите метод post () для отправки события, передайте объект Object
2. Получите объект PostingThreadState, который мы упомянули в начале, добавьте событие в список внутри и оцените текущее состояние.

    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. В соответствии с типом события найдите в коллекции, существует ли метод подписки, который подписывается на этот тип события, если он не найден, выбросить исключение; если обнаружено, обработать каждую операцию подписки в цикле.

            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. В финальной рассылке выберите соответствующую рассылку постеров в соответствии с веткой, выбранной методом подписки.

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

выполненный

Реализация относительно проста, с использованием механизма отражения Java.

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

Несколько плакатов

В каком потоке было получено событие, зависит от того, какой плакат используется для его отправки. По умолчанию оно выполняется напрямую. Основной поток выполняется с использованием HandlerPoster, фоновое выполнение использует BackgroundPoster, а асинхронное выполнение использует AsyncPoster.
Сначала взгляните на Poster, класс оправдания для публичной реализации:

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

Есть метод выполнения, который заключается в отправке события

AsyncPoster

Асинхронный плакат реализует интерфейсы Runnable и Poster и использует Runnable, чтобы открыть новый поток для выполнения.
Когда выполняется метод enqueue, метод run (), который должен быть реализован в Runnable, выполняется внутри. Это новый поток для выполнения. Метод отражения invokeSubscriber () в EventBus вызывается в потоке.


Разница между BackgroundPoster и AsyncPoster заключается в том, что он открывает только один поток. Когда возникает событие и его необходимо распространить, он сначала определяет, выполняются ли в данный момент другие операции. Если он свободен, выполните его немедленно, в противном случае сначала вставьте список и очередь и дождитесь обработки предыдущего события. Тогда оформляйте следующее событие.

HandlerPoster
- это главный плакат потока в конце.Когда этот плакат создается, передается Looper.getMainLooper () , поэтому он осуществляет мониторинг в основном потоке. Этот плакат использует режим обработчика. Когда он получает запрос на отправку сообщения, он вызывает свой собственный метод sendMessage (), а затем обрабатывает запрос в 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;
        }
    }

Конец

На этом весь процесс EventBus в основном завершен. Эта статья начинается с анализа процесса и в основном объясняет всю структуру EventBus и принципы внутренней реализации. В этом есть много недостатков. Пожалуйста, просветите меня.
спасибо!

рекомендация

отblog.csdn.net/lizebin_bin/article/details/80409169
рекомендация