Guava EventBus задерживает выполнение обработчиком вложенных событий - PullRequest
0 голосов
/ 03 ноября 2018

Моя проблема очень похожа на отправку Guava EventBus , но, хотя основная проблема похожа, моя попытка исправить меня оставила меня в темноте.

У меня есть 2 события, которые запускаются спина к спине. Второе событие зависит от конечного состояния первого события после того, как все обработчики покончили с ним. Он должен срабатывать только в том случае, если 1-е событие не было отменено. Подвох в том, что оба эти события запускаются из обработчика другого события.

Так что, хотя мне все равно, кто слушает первое вложенное событие, мне все равно, что они скажут об этом. Я уже покидаю пространство проблем, которое пытаются решить события и EventBus в Guava?

Учитывая:

public void parentEventHandler(ParentEvent parentEvent) {
    Object nestedEvent = createNestedEvent();
    eventBus.post(nestedEvent);

    if(nestedEvent.isCancelled()) {
        return;
    }

    Object anotherNestedEvent = createOtherNestedEvent();
    eventBus.post(anotherNestedEvent);
}

То, что я ожидал:

1. parentEvent is posted
2. parentEventHandler is called
3. nestedEvent is posted
4. handlers of nestedEvent are called
5. finished handling nestedEvent
6. if statement checks for cancel state of nestedEvent
7.     anotherNestedEvent is posted if nestedEvent not cancelled
8.     handlers of anotherNestedEvent are called
9.     finished handling anotherNestedEvent
10 finished handling parentEvent

Что происходит:

1. parentEvent is posted
2. parentEventHandler is called
3. nestedEvent is posted
4. if statement checks for cancel state of nestedEvent (defaults to false)
5.     anotherNestedEvent is posted
6. finished handing parentEvent
7. handlers of nestedEvent are called
8. nestedEvent is cancelled (too late now)
9. finished handling nestedEvent
10 handlers of anotherNestedEvent are called
11 finished handling anotherNestedEvent

В пункте 8. независимо от того, отменяет ли обработчик событие, второе событие уже было поставлено в очередь, поскольку проверка отмены по умолчанию имеет значение false. EventBus Guava настаивает на том, чтобы завершить текущий запуск обработчика перед началом следующего события, которое, я уверен, имеет свое применение, но не то, что я ищу.

Попытка взлома:

Я заметил, что в Guava есть реализация для ImmediateDispatcher (https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L179), которая публикует события, когда они вступают в противоречие с поведением сохранения событий, пока текущее событие не будет обработано всеми подписчиками PerThreadQueuedDispatcher по умолчанию ( https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L73).

Однако эти другие диспетчеры являются частными пакетами, и на EventBus нет общедоступного API для изменения используемого диспетчера. Разветвление Guava и изменение диспетчера по умолчанию на https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/EventBus.java#L122 и L136 на Dispatcher.immediate(), переустановка Guava локально под другим номером версии и толстый резкий удар по этой пользовательской сборке в моем проекте наблюдаемого поведения событий в приложении т изменилось вообще. И теперь я полностью потерян.

Есть ли способ добиться строгой диспетчеризации событий LIFO с помощью EventBus в Guava или есть другая парадигма, на которую я должен смотреть, а не события, которые имели бы больше смысла, когда события могут быть отменены и часто оказываются вложенными в другие обработчики событий? Мне все равно, сколько и какие подписчики слушают события, но я хочу знать, что они должны сказать о событии (то есть, решили ли они, отменить его или нет). Приложение полностью однопоточное.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Оказывается, моя пользовательская сборка Guava была перезаписана уже связанной более старой версией Guava из хост-приложения (конечно, это было сделано). Я закончил тем, что применил взлом через код (вероятно, не очень устойчивый к изменениям версии):

public GuavaEventService() {
    this.bus = new EventBus();
    try {
        Field dispatcherField = EventBus.class.getDeclaredField("dispatcher");
        dispatcherField.setAccessible(true);
        Class<?> dispatcherClass = dispatcherField.get(this.bus).getClass().getSuperclass();
        Method immediateDispatcher = dispatcherClass.getDeclaredMethod("immediate");
        immediateDispatcher.setAccessible(true);
        dispatcherField.set(this.bus, immediateDispatcher.invoke(null));
    } catch (Exception ex) {
        throw new IllegalStateException("Failed to initialize event service dispatcher: " + ex.getMessage());
    }
}

Тем не менее, Эйден предложил намного более чистую альтернативу шины событий Async при редактировании своего ответа: EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());

0 голосов
/ 04 ноября 2018

Если вы используете «обычный» EventBus, вы можете заставить это работать, создав вторичное событие.

Добавить интерфейс InternalEventCallback и класс InternalEventCallbackHandler:

interface InternalEventCallback {
    void run();
}

class InternalEventCallbackHandler {

    @Subscribe
    public void internalEventHandler(InternalEventCallback r){
        r.run();
    }
}

Там, где вы создаете EventBus, зарегистрируйте InternalEventCallbackHandler:

eventBus.register(new InternalEventCallbackHandler());

Тогда в вашем parentEventHandler сделать:

@Subscribe
public void parentEventHandler(ParentEvent parentEvent) {
    NestedEvent nestedEvent = createNestedEvent();
    eventBus.post(nestedEvent);
    eventBus.post(new InternalEventCallback() {
        @Override
        public void run() {
            if(nestedEvent.isCancelled()) {
                return;
            }

            Object anotherNestedEvent = createOtherNestedEvent();
            eventBus.post(anotherNestedEvent);
        }
    });
}

Редактировать:

Если вы используете AsyncEventBus вместе с «непосредственным исполнителем», вы можете получить то же поведение, что и в примере выше, но без InternalEventCallback и InternalEventCallbackHandler

EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());
...