EventBus是一个很棒的工具,它可用来对程序组件进行解耦。在接下来的几篇文章中我将会阐述如何通过它来简化代码,提高可读性以及简化测试。但在本文中我想首先讨论一下为什么我会选择使用EventBus。特别是我会将其与同类技术进行对比。
对比Java监听器接口(Listener Interfaces)
在Java中,特别是Android,一个常用的模式就是使用”监听器(Listeners)”接口。在此模式中,一个实现了监听器接口的类必须将自身注册到它想要监听的类中去。这就意味着监听与被监听之间属于强关联关系。这种关系就使得单元测试很难进行开展。Jake Wharton在他的《使用Otto解耦Android应用间通信》这篇文章中很好地阐述了这个问题,在此我就不进行展开了。
对比本地广播管理器(LocalBroadcastManager)
另一项技术就是在组件间通过本地广播管理器(LocalBroadcastManager)进行消息的发送与监听。虽然这对于解耦有很好的帮助,但它的API不如EventBus那样简洁。此外,如果你不注意保持Intent extras类型的一致,它还可能引发潜在的运行时/类型检测错误。
考虑下面的应用场景:
- 组件A执行一个批量异步数据库更新操作。当成功完成时,它发送一条通知告诉更新了多少条记录。发生错误时,发送一条通知包含了错误信息。
- 组件B则需要知道到更新是否完成和更新了多少条记录,以及是否有错误发生和错误信息。
使用LocalBroadcastManager,最简单的代码大概如下面所示。
组件A:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// when update completes successfully
Intent
intent
=
new
Intent
(
)
;
intent
.
setAction
(
FILTER_ACTION_UPDATE_SUCCESS
)
;
intent
.
putExtra
(
EXTRA_COUNT
,
count
)
;
LocalBroadcastManager
.
getInstance
(
getContext
(
)
)
.
sendBroadcast
(
intent
)
;
.
.
.
// when there is an error
Intent
intent
=
new
Intent
(
)
;
intent
.
setAction
(
FILTER_ACTION_UPDATE_ERROR
)
;
intent
.
putExtra
(
EXTRA_ERROR_MSG
,
error
.
getMessage
(
)
)
;
LocalBroadcastManager
.
getInstance
(
getContext
(
)
)
.
sendBroadcast
(
intent
)
;
|
组件B:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
IntentFilter
updateSuccessFilter
=
new
IntentFilter
(
FILTER_ACTION_UPDATE_SUCCESS
)
;
BroadcastReceiver
updateSuccessReceiver
=
new
BroadcastReceiver
(
)
{
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
int
updateCount
=
intent
.
getIntExtra
(
EXTRA_COUNT
,
-
1
)
;
// TODO - do something with the count
}
}
;
IntentFilter
updateErrorFilter
=
new
IntentFilter
(
FILTER_ACTION_UPDATE_ERROR
)
;
BroadcastReceiver
updateErrorReceiver
=
new
BroadcastReceiver
(
)
{
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
String
errorMsg
=
intent
.
getStringExtra
(
EXTRA_ERROR_MSG
)
;
// TODO - display error message or similar
}
}
;
.
.
.
getContext
(
)
.
registerReceiver
(
updateSuccessReceiver
,
updateSuccessFilter
)
;
getContext
(
)
.
registerReceiver
(
updateErrorReceiver
,
updateErrorFilter
)
;
.
.
.
getContext
(
)
.
unregisterReceiver
(
updateSuccessReceiver
)
;
getContext
(
)
.
unregisterReceiver
(
updateErrorReceiver
)
;
|
使用EventBus,同样功能实现可以表示如下。(注:此例中我使用的是我的EventBus实现,参见Green Robot的EventBus。)
Event类:
1
2
3
4
5
6
7
|
public
class
UpdateCompleteEvent
{
public
final
int
count
;
UpdateCompleteEvent
(
int
count
)
{
this
.
count
=
count
;
}
}
|
1
2
3
4
5
6
7
|
public
class
UpdateErrorEvent
{
public
final
String
message
;
UpdateCompleteEvent
(
String
message
)
{
this
.
message
=
message
;
}
}
|
组件A:
1
2
3
4
5
6
7
8
9
10
|
EventBus
bus
=
EventBus
.
getDefault
(
)
;
// Or better yet, inject it
.
.
.
// when update completes successfully
bus
.
post
(
new
UpdateCompleteEvent
(
count
)
)
;
.
.
.
// when there is an error
bus
.
post
(
new
UpdateErrorEvent
(
error
.
getMessage
(
)
)
)
;
|
组件B:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
EventBus
bus
=
EventBus
.
getDefault
(
)
;
// Or better yet, inject it
.
.
.
public
void
onEvent
(
UpdateCompleteEvent
event
)
{
// TODO - do something with event.count
}
public
void
onEvent
(
UpdateErrorEvent
event
)
{
// TODO - do something with event.message
}
.
.
.
bus
.
register
(
this
)
;
.
.
.
bus
.
unregister
(
this
)
;
|
如你所见,使用EventBus不仅使代码变得清晰,而且增强了类型安全(type-safe)。当用Intent传递数据时,在编译时并不能检查出所设的extra类型与收到时的类型一致。所以一个很常见的错误便是你或者你团队中的其他人改变了Intent所传递的数据,但忘记了对全部的接收器(receiver)进行更新。这种错误在编译时是无法被发现的,只有在运行时才会发现问题。
而使用EventBus所传递的消息则是通过你所定义的Event类。由于接收者方法是直接与这些类实例打交道,所以所有的数据均可以进行类型检查,这样任何由于类型不一致所导致的错误都可以在编译时刻被发现。
另外就是你的Event类可以定义成任何类型。我通常会为了表示事件而显式地创建明确命名的类,你也通过EventBus发送/接收任何类。通过这种方法,你就不必受限于那些只能添加到Intent extras中的简单数据类型了。例如,你可以发送一个和ORM(注1)模型类实例,并且在接收端直接处理与ORM操作相关的类实例。
希望我这里给出了一个在Android中使用EventBus的强有力的应用场景案例。在下一篇文章中我会谈谈我是如何使用Green Robot的EventBus中的“sticky”事件的。
注1: ORM(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping)对象关系映射,是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。