Guava EventBus 延迟嵌套事件的处理程序执行
Guava EventBus delaying handler execution of nested events
我的问题与 Guava EventBus dispatching 完全相同,但虽然根本问题很相似,但我尝试的修复让我一头雾水。
我有 2 个事件连续触发。在所有处理程序完成后,第二个事件取决于第一个事件的最终状态。它应该只在第一个事件没有被取消时触发。要注意的是,这两个事件都是从另一个事件的处理程序中触发的。
因此,虽然我不在乎谁会收听第一个嵌套事件,但我确实在乎他们对此有何评论。我是否已经离开了事件和 Guava 的 EventBus 旨在解决的问题空间?
正在考虑:
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。 Guava 的 EventBus 坚持在开始下一个事件之前完成其当前处理程序 运行,我确信它有它的用处,但它不是我正在寻找的。
尝试破解:
我注意到 Guava 有一个 ImmediateDispatcher (https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L179) available that publishes events as they come in contrary to the behaviour of saving events until the current one has been handled by all subscribers of the default PerThreadQueuedDispatcher (https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L73) 的实现。
但是,这些其他调度程序是包私有的,并且 EventBus 上没有 public API 来更改要使用的调度程序。分叉 Guava 并将 https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/EventBus.java#L122 和 L136 处的默认调度程序更改为 Dispatcher.immediate()
,在本地以另一个版本号重新安装 Guava,并在我的项目中自定义构建应用程序中观察到的事件行为根本没有改变。现在我完全迷路了。
有没有一种方法可以使用 Guava 的 EventBus 实现严格的 LIFO 事件调度,或者是否有一个不同的范例我应该看看,而不是当事件可以被取消并经常嵌套在其他事件中时更有意义的事件处理程序?我不在乎有多少订阅者和哪些订阅者收听这些事件,但我确实想知道他们对事件有什么看法(即他们是否决定是否应该取消事件)。该应用程序完全是单线程的。
如果您正在使用 "regular" EventBus
,您可以通过创建次要事件来实现它。
添加接口 InternalEventCallback
和 class 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
与 "direct executor" 一起使用,您可以获得与上述示例相同的行为,但没有 InternalEventCallback
和 InternalEventCallbackHandler
EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());
原来我的自定义 Guava 构建被主机应用程序中已经捆绑的旧版本 Guava 覆盖(当然是这样)。我最终通过代码应用了 hack(可能对版本更改不是很有弹性):
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());
}
}
但是,eiden 在编辑他的回答时建议使用更简洁的异步事件总线替代方案:
EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());
我的问题与 Guava EventBus dispatching 完全相同,但虽然根本问题很相似,但我尝试的修复让我一头雾水。
我有 2 个事件连续触发。在所有处理程序完成后,第二个事件取决于第一个事件的最终状态。它应该只在第一个事件没有被取消时触发。要注意的是,这两个事件都是从另一个事件的处理程序中触发的。
因此,虽然我不在乎谁会收听第一个嵌套事件,但我确实在乎他们对此有何评论。我是否已经离开了事件和 Guava 的 EventBus 旨在解决的问题空间?
正在考虑:
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。 Guava 的 EventBus 坚持在开始下一个事件之前完成其当前处理程序 运行,我确信它有它的用处,但它不是我正在寻找的。
尝试破解:
我注意到 Guava 有一个 ImmediateDispatcher (https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L179) available that publishes events as they come in contrary to the behaviour of saving events until the current one has been handled by all subscribers of the default PerThreadQueuedDispatcher (https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/Dispatcher.java#L73) 的实现。
但是,这些其他调度程序是包私有的,并且 EventBus 上没有 public API 来更改要使用的调度程序。分叉 Guava 并将 https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/EventBus.java#L122 和 L136 处的默认调度程序更改为 Dispatcher.immediate()
,在本地以另一个版本号重新安装 Guava,并在我的项目中自定义构建应用程序中观察到的事件行为根本没有改变。现在我完全迷路了。
有没有一种方法可以使用 Guava 的 EventBus 实现严格的 LIFO 事件调度,或者是否有一个不同的范例我应该看看,而不是当事件可以被取消并经常嵌套在其他事件中时更有意义的事件处理程序?我不在乎有多少订阅者和哪些订阅者收听这些事件,但我确实想知道他们对事件有什么看法(即他们是否决定是否应该取消事件)。该应用程序完全是单线程的。
如果您正在使用 "regular" EventBus
,您可以通过创建次要事件来实现它。
添加接口 InternalEventCallback
和 class 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
与 "direct executor" 一起使用,您可以获得与上述示例相同的行为,但没有 InternalEventCallback
和 InternalEventCallbackHandler
EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());
原来我的自定义 Guava 构建被主机应用程序中已经捆绑的旧版本 Guava 覆盖(当然是这样)。我最终通过代码应用了 hack(可能对版本更改不是很有弹性):
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());
}
}
但是,eiden 在编辑他的回答时建议使用更简洁的异步事件总线替代方案:
EventBus eventBus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());