在 Kotlin 中处理事件总线的通用监听器
Handle generic listeners of event bus in Kotlin
我的目标是在没有任何第 3 方库的情况下在 Kotlin 中实现非常简单的事件总线。我用下面的代码完成了。
class EventListener<T>(
val owner: Any,
val event: Class<T>,
val callback: (T) -> Unit
)
interface IEventBus {
fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit)
fun unsubscribe(owner: Any)
fun <T> push(event: T)
}
class EventBus : IEventBus {
private val _listeners = mutableListOf<EventListener<*>>()
override fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit) {
val listener = EventListener(owner, event, callback)
_listeners.add(listener)
}
override fun unsubscribe(owner: Any) {
_listeners.removeAll {
it.owner == owner
}
}
override fun <T> push(event: T) {
_listeners.forEach { listener ->
try {
val l = listener as EventListener<T> // is always a success
l.callback(event) // throws an exception if can't handle the event
} catch (ex: Exception) { }
}
}
}
然后用法是这样的:
// register listener
bus.subscribe(this, String::class.java) {
print(it)
}
// push an event (from somewhere else in the project)
bus.push("Hello world!")
它可以工作并且完全可用,但是我对它不满意...将 listener 转换为 EventListener 将始终 return something 然后如果 l.callback(event) 无法处理事件类型它会抛出异常。因此,如果订阅了很多侦听器,那么它将生成许多不需要的异常,这些异常将被忽略。
我更愿意先做一些检查,例如:
if (listener is EventListener<T>)
listener.callback(event)
但我发现 JVM 在编译后会丢失有关泛型类型的信息。我还发现可以使用 kotlin 的 inline 和 reified 绕过它,但是它们不能用于来自接口的方法。 ..
所以我的问题是你知道处理这种一般问题的更优雅的方法吗?
由于您已经公开了事件 (EventListener#event
) 的 class,您可以使用 isInstance()
检查 class 是否与实例兼容您的活动。
所以,而不是:
if (listener is EventListener<T>)
listener.callback(event)
你可以做到:
if (listener.event.isInstance(event)) {
// The cast is safe since you checked if the event can be received by the listener.
(listener as EventListener<T>).callback(event)
}
基元
如果你也想支持 T
原始类型,你可以将 Class<T>
更改为 KClass<T>
或者手动检查每个原始类型的实例(例如 event is Int
, event is Long
).
kotlinx.coroutines.flow.SharedFlow 的文档包含一个简单示例:
SharedFlow is useful for broadcasting events that happen inside an
application to subscribers that can come and go. For example, the
following class encapsulates an event bus that distributes events to
all subscribers in a rendezvous manner, suspending until all
subscribers process each event:
class EventBus {
private val _events = MutableSharedFlow<Event>() // private mutable shared flow
val events = _events.asSharedFlow() // publicly exposed as read-only shared flow
suspend fun produceEvent(event: Event) {
_events.emit(event) // suspends until all subscribers receive it
}
}
我的目标是在没有任何第 3 方库的情况下在 Kotlin 中实现非常简单的事件总线。我用下面的代码完成了。
class EventListener<T>(
val owner: Any,
val event: Class<T>,
val callback: (T) -> Unit
)
interface IEventBus {
fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit)
fun unsubscribe(owner: Any)
fun <T> push(event: T)
}
class EventBus : IEventBus {
private val _listeners = mutableListOf<EventListener<*>>()
override fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit) {
val listener = EventListener(owner, event, callback)
_listeners.add(listener)
}
override fun unsubscribe(owner: Any) {
_listeners.removeAll {
it.owner == owner
}
}
override fun <T> push(event: T) {
_listeners.forEach { listener ->
try {
val l = listener as EventListener<T> // is always a success
l.callback(event) // throws an exception if can't handle the event
} catch (ex: Exception) { }
}
}
}
然后用法是这样的:
// register listener
bus.subscribe(this, String::class.java) {
print(it)
}
// push an event (from somewhere else in the project)
bus.push("Hello world!")
它可以工作并且完全可用,但是我对它不满意...将 listener 转换为 EventListener 将始终 return something 然后如果 l.callback(event) 无法处理事件类型它会抛出异常。因此,如果订阅了很多侦听器,那么它将生成许多不需要的异常,这些异常将被忽略。
我更愿意先做一些检查,例如:
if (listener is EventListener<T>)
listener.callback(event)
但我发现 JVM 在编译后会丢失有关泛型类型的信息。我还发现可以使用 kotlin 的 inline 和 reified 绕过它,但是它们不能用于来自接口的方法。 ..
所以我的问题是你知道处理这种一般问题的更优雅的方法吗?
由于您已经公开了事件 (EventListener#event
) 的 class,您可以使用 isInstance()
检查 class 是否与实例兼容您的活动。
所以,而不是:
if (listener is EventListener<T>)
listener.callback(event)
你可以做到:
if (listener.event.isInstance(event)) {
// The cast is safe since you checked if the event can be received by the listener.
(listener as EventListener<T>).callback(event)
}
基元
如果你也想支持 T
原始类型,你可以将 Class<T>
更改为 KClass<T>
或者手动检查每个原始类型的实例(例如 event is Int
, event is Long
).
kotlinx.coroutines.flow.SharedFlow 的文档包含一个简单示例:
SharedFlow is useful for broadcasting events that happen inside an application to subscribers that can come and go. For example, the following class encapsulates an event bus that distributes events to all subscribers in a rendezvous manner, suspending until all subscribers process each event:
class EventBus {
private val _events = MutableSharedFlow<Event>() // private mutable shared flow
val events = _events.asSharedFlow() // publicly exposed as read-only shared flow
suspend fun produceEvent(event: Event) {
_events.emit(event) // suspends until all subscribers receive it
}
}