Android 处理Kotlin中事件总线的通用侦听器

Android 处理Kotlin中事件总线的通用侦听器,android,generics,events,kotlin,jvm,Android,Generics,Events,Kotlin,Jvm,我的目标是在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>

我的目标是在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) { }
        }
    }
}
它的工作,是完全可用的,但我不满意它。。。将侦听器转换为EventListener将始终返回某些内容,如果l.callback(event)无法处理事件类型,它将抛出异常。因此,如果有许多侦听器订阅,那么它将生成许多不需要的异常,这些异常将被忽略

我更愿意先做一些检查,比如:

if (listener is EventListener<T>)
    listener.callback(event)
if(侦听器是EventListener)
回调(事件)
但是我发现JVM在编译之后丢失了关于泛型类型的信息。我还发现,使用kotlin的内联具体化可以绕过它,但是这些不能用于来自接口的方法


因此,我的问题是,您知道处理此类泛型问题的更优雅的方法吗?

既然您已经公开了事件的类(
EventListener\event
),您可以使用
isInstance()
检查该类是否与事件实例的赋值兼容

因此,不是:

if (listener is EventListener<T>)
    listener.callback(event)
if(侦听器是EventListener)
回调(事件)
你可以做:

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)
}
if(listener.event.isInstance(event)){
//强制转换是安全的,因为您检查了侦听器是否可以接收事件。
(侦听器作为EventListener)。回调(事件)
}
基本体

如果您想支持原语类型的
T
,可以将
Class
更改为
KClass
,或者手动检查每个原语类型的实例(例如
事件为Int
事件为Long
)。

的文档包含一个简单的示例:

SharedFlow对于广播在内部发生的事件非常有用 应用程序到可以来去的订阅者。例如 下面的类封装了一个事件总线,该总线将事件分发给 所有订阅者会合,暂停,直到所有订阅者 订阅服务器处理每个事件:

class事件总线{
private val _events=MutableSharedFlow()//私有可变共享流
val events=\u events.asSharedFlow()//作为只读共享流公开
暂停趣味产品事件(事件:事件){
_events.emit(event)//挂起,直到所有订阅者都收到它
}
}

此外,也很有用。

但是它似乎在Int.Int::class.java.isInstance(5)方面有问题false@DawidGrajewski这是因为Kotlin中的
Int
既可以引用原语Java
Int
,也可以引用装箱的Java
Integer
。如果您想支持原语,有两种可能:用
KClass
替换
Class
,或者检查
事件是否可分配给原语类型。例如,
事件为Int
事件为Long
。如果可以,显然我建议您将
Class
更改为
KClass
,因为这样可以避免对基本类型进行所有检查。再次感谢您!这对我来说就结束了。祝你过得愉快:)
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)
}
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
    }
}