Why NextEvents
总会有人说Guava.EventBus和Otto,还有Greenbot.EventBus已经非常优秀,你再造轮子有什么鬼用?
因为他们是圆轮子,我想要方的。他们没有,我就自己造啦!
Greenbot.EventBus、Guava.EventBus和Otto类似,对@Subscribe注解的方法进行回调,并根据方法参数类型来过滤。NextEvents 基于它们的设计思想,并且在回调调度粒度、过滤方式等做了改进,适应项目的需要。
在此前Android项目实践中使用到Otto,它非常好地解耦一些模块之间的弱关联。但过程中,我发现Otto未能解决遇到的问题:
-
我需要在@Subscribe方法中自行决定回调内部的执行线程,而不是由调用者或总线本身决定。可以在@Subscriber注解中声明调度方式是最好的。我在其它相似的库中也看到同样的实现,看来大家的思想都是一致的。
-
项目中多个不同逻辑的回调方法,其参数是相同的。Otto基于类型过滤,使得我只能在回调之后再过滤一次。我需要在定义方法时就决定它只接受哪一类型的事件。使用消息名来标识和过滤,与传统Web后端的消息总线相同。
-
触发执行一个方法,前提是多个任务的处理结果。(这是未来版本多事件触发的特性,现在还没实现)
已支持特性
- 支持使用 <EventHandler> 接口回调;
- 支持使用 @Subscribe 注解方法回调;
- 支持多种回调方式: CALLER_THREAD / IO_THREAD / MAIN_THREAD (For Android only)
- 支持自定义回调目标的调度处理 <Scheduler>
- 支持事件组
未来支持特性
- 多事件
- 自定义事件匹配处理
- 事件转换
触发条件
在 <EventHandler> 接口回调模式下,所有 <EventFilter> 返回 True 即触发回调。
在 @Subscribe 注解模式下,触发回调方法需要满足两个条件:
- 定义的事件名与发送事件名相同;
- 定义的参数与发送事件负载,数量相同且类型相同;
对于条件2
,以下两个特殊情况也满足:
- 定义的参数数量为0;
- 定义唯一参数且类型为 Any;
// 当发生"str-event"事件,并且事件类型为String时,将被回调;@Subscribe(events = "str-event")void handleWithArgEvents(String event) { ...}// 定义参数数量为0: 当发生"str-event"事件时,无论事件负载是什么类型,都被回调。@Subscribe(events = "str-event")void handleNoArgEvent( ) { ...}// 定义唯一参数且类型为Any: 当发生"str-event"事件时,事件负载为任意数量、任意类型,都被回调。@Subscribe(events = "str-event")void handleAnyTypeEvent(Any events) { Object[] payloadValues = events.values; Class[] payloadTypes = events.types; ...}// 发射 str-event 事件,上面定义的全部方法都触发回调nextEvents.emit("str-event", new String("this is an event payload"))
多负载事件
有些事件可能有多个负载,NextEvents可以直接提交多个负载,无须为这些负载创建包装类。
// 当发生事件时,会将参数按参数类型顺序依次填充@Subscribe(events = "profile-event")void handleEvent(String name, int age, float weight) { ...}// 发射事件,回调方法的参数将会按参数类型顺序依次填充这些数值nextEvents.emit("profile-event", "yoojia", 18, 1024.0f)
多负载参数顺序
NextEvents对多负载事件支持乱序参数。在符合触发条件的前提下,上面代码为例,以下的方法都会触发回调:
@Subscribe(events = "profile-event")void handleEvent1(int age, float weight, String name) { ...}@Subscribe(events = "profile-event")void handleEvent2(float weight, int age, String name) { ...}
乱序符合以下规则:
- 各个参数类型互不相同时,可以是任意顺序。
- 存在相同参数类型的,填充顺序按 emit() 中的参数顺序。如:
@Subscribe(events = "same-type-event")void handleEvent(int height, int age, String name) { // height == 1024 --> TRUE // age == 18 --> TRUE ...}@Subscribe(events = "same-type-event")void handleEvent(int age, int height, String name) { // age == 1024 --> TRUE // height == 18 --> TRUE ...}nextEvents.emit("same-type-event", 1024, 18, "yoojia")
Dead event
如果发送事件没有回调目标方法或者Handler时,原事件将被包装成DeadEvent重新发送。可以通过订阅EventPayload.DEAD_EVENT
事件来处理DeadEvent。
DeadEvent事件与普通事件一样,唯一区别是它的事件名固定为EventPayload.DEAD_EVENT
的值。
@Subscribe(events = EventPayload.DEAD_EVENT)void onMissedTyped(String evt) { ...}
Benchmark
v2.0
在 OS X 10.11.3' / 2.6 GHz Intel Core i5 / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
环境下的性能对比情况如下表:
测试类型/负载方式 | TPS/QPS | 总投递时间 | 总运行时间 | 投递事件量 |
---|---|---|---|---|
MultiThreads(1ms Payload) | 2749 | 47ms | 727ms | 2000 |
SharedThreads(1ms Payload) | 3012 | 8ms | 663ms | 2000 |
CallerThread(1ms Payload) | 739 | 2705ms | 2705ms | 2000 |
GuavaEvents(1ms Payload) | 717 | 2785ms | 2785ms | 2000 |
MultiThreads(Nop Payload) | 1947582 | 1026ms | 1026ms | 2000000 |
SharedThreads(Nop Payload) | 891257 | 2243ms | 2244ms | 2000000 |
CallerThread(Nop Payload) | 3933523 | 508ms | 508ms | 2000000 |
GuavaEvents(Nop Payload) | 2083789 | 959ms | 959ms | 2000000 |
v2.0
在 Ubuntu 14.04 LTS / 3.6 GHz AMD A8-5600K / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
环境下的性能对比情况如下:
测试类型/负载方式 | TPS/QPS | 总投递时间 | 总运行时间 | 投递事件量 |
---|---|---|---|---|
MultiThreads(1ms Payload) | 3242 | 22ms | 616ms | 2000 |
SharedThreads(1ms Payload) | 3351 | 3ms | 596ms | 2000 |
CallerThread(1ms Payload) | 914 | 2187ms | 2187ms | 2000 |
GuavaEvents(1ms Payload) | 890 | 2245ms | 2245ms | 2000 |
MultiThreads(Nop Payload) | 1078525 | 1846ms | 1854ms | 2000000 |
SharedThreads(Nop Payload) | 1373540 | 1427ms | 1456ms | 2000000 |
CallerThread(Nop Payload) | 3834583 | 521ms | 521ms | 2000000 |
GuavaEvents(Nop Payload) | 1578518 | 1266ms | 1267ms | 2000000 |
v2.0
在 Windows 10 64x / 3.2 GHz Intel i5-4460 / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
环境下的性能对比情况如下:
测试类型/负载方式 | TPS/QPS | 总投递时间 | 总运行时间 | 投递事件量 |
---|---|---|---|---|
MultiThreads(1ms Payload) | 3447 | 10ms | 580ms | 2000 |
SharedThreads(1ms Payload) | 3376 | 1ms | 592ms | 2000 |
CallerThread(1ms Payload) | 902 | 2215ms | 2215ms | 2000 |
GuavaEvents(1ms Payload) | 884 | 2260ms | 2260ms | 2000 |
MultiThreads(Nop Payload) | 1935508 | 1033ms | 1033ms | 2000000 |
SharedThreads(Nop Payload) | 2261975 | 882ms | 884ms | 2000000 |
CallerThread(Nop Payload) | 4602303 | 434ms | 434ms | 2000000 |
GuavaEvents(Nop Payload) | 3980131 | 502ms | 502ms | 2000000 |