Лучший способ комбинировать обработку потока событий guava eventbus и AWT Event

Когда у вас есть асинхронная шина событий и события пожара, скажем, в модели, которая попадает в пользовательский интерфейс, у вас, вероятно, есть следующая проблема:

Зарегистрированный обработчик выполняется в рабочем потоке, но все изменения качания пользовательского интерфейса должны выполняться в потоке событий AWT. Это означает, что вам нужно конвертировать весь ваш код обработчика в EventQueue.invokeLater(...).

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

Как насчет расширения к шине событий guava, которая помещает обработчик для выполнения в специальном потоке? Это может быть отмечено аннотацией, например. @ExecuteWithinEDT:

class EventBusChangeRecorder {
  @Subscribe @ExecuteWithinEDT void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }
}

Ответ 1

Обработчики, зарегистрированные в шине событий async, выполняются в любом потоке, предоставленном Executor для их запуска, а не в рабочем потоке.

Что я сделал, создается реализация Executor, которая запускает файлы в потоке очереди событий. Это довольно просто:

public class EventQueueExecutor implements Executor {
  @Override public void execute(Runnable command) {
    EventQueue.invokeLater(command);
  }
}

Затем вы можете просто создать свой EventBus с помощью этого:

EventBus eventBus = new AsyncEventBus(new EventQueueExecutor());

Затем все обработчики будут выполняться в потоке очереди событий.

Edit:

Пример событий пересылки:

public class EventForwarder {
  private final EventBus uiEventBus;

  public EventForwarder(EventBus uiEventBus) {
    this.uiEventBus = uiEventBus;
  }

  // forward all events
  @Subscribe
  public void forwardEvent(Object event) {
    uiEventBus.post(event);
  }

  // or if you only want a specific type of event forwarded
  @Subscribe
  public void forwardEvent(UiEvent event) {
    uiEventBus.post(event);
  }
}

Просто подпишитесь на свою основную шину событий и разместите все события на главной шине событий, но подпишитесь на все компоненты пользовательского интерфейса на шину событий пользовательского интерфейса.

Ответ 2

Вы можете создать EventBus, который отправляется только по AWT-потоку:

EventBus mybus = new AsyncEventBus("awt",
    new Executor() {
        public void execute (Runnable cmd) {
            if (EventQueue.isDispatchThread()) {
                cmd.run();
            } else {
                EventQueue.invokeLater(cmd);
            }
        }
    });