subscribe
创建一个事件监听器, 监听事件通道中所有 E 及其子类事件.
创建监听
调用本函数:
eventChannel.subscribe<E> { /* 会收到此通道中的所有是 E 的事件 */}
生命周期
通过协程作用域管理监听器
本函数将会创建一个 Job, 成为 parentJob 中的子任务. 可创建一个 CoroutineScope 来管理所有的监听器:
val scope = CoroutineScope(SupervisorJob())
val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel
scopedChannel.subscribeAlways<MemberJoinEvent> { /* ... */} // 启动监听, 监听器协程会作为 scope 的子任务
scopedChannel.subscribeAlways<MemberMuteEvent> { /* ... */} // 启动监听, 监听器协程会作为 scope 的子任务
scope.cancel() // 停止了协程作用域, 也就取消了两个监听器
这个函数返回 Listener, 它是一个 CompletableJob. 它会成为 parentJob 或 parentScope 的一个 子任务
停止监听
如果 handler 返回 ListeningStatus.STOPPED 监听器将被停止.
也可以通过 subscribe 返回值 Listener 的 Listener.complete
监听器调度
监听器会被创建一个协程任务, 语义上在 parentScope 下运行. 通过 Kotlin 默认协程调度器 在固定的全局共享线程池里执行, 除非有 coroutineContext 指定.
默认在 handler 中不能处理阻塞任务. 阻塞任务将会阻塞一个 Kotlin 全局协程调度线程并可能导致严重问题. 请通过 withContext(Dispatchers.IO) { }
等方法执行阻塞工作.
异常处理
监听过程抛出的异常是需要尽可能避免的, 因为这将产生不确定性.
当参数 handler 处理事件抛出异常时, 只会从监听方协程上下文 (CoroutineContext) 寻找 CoroutineExceptionHandler 处理异常, 即如下顺序:
本函数参数 coroutineContext
若以上步骤无法获取 CoroutineExceptionHandler, 则只会在日志记录异常. 因此建议先指定 CoroutineExceptionHandler (可通过 EventChannel.exceptionHandler) 再监听事件, 或者在监听事件中捕获异常.
因此, 广播方 (Event.broadcast) 不会知晓监听方产生的异常, 其 Event.broadcast 过程也不会因监听方产生异常而提前结束.
备注: 在 2.11 以前, 发生上述异常时还会从广播方和有关 Bot 协程域获取 CoroutineExceptionHandler. 因此行为不稳定而在 2.11 变更为上述过程.
事件处理时抛出异常不会停止监听器.
建议在事件处理中 (即 handler 里) 处理异常, 或在参数 coroutineContext 中添加 CoroutineExceptionHandler, 或通过 EventChannel.exceptionHandler.
并发安全性
基于 concurrency 参数, 事件监听器可以被允许并行执行.
若 concurrency 为 ConcurrencyKind.CONCURRENT, handler 可能被并行调用, 需要保证并发安全.
若 concurrency 为 ConcurrencyKind.LOCKED, handler 会被 Mutex 限制, 串行异步执行.
衍生监听方法
这些方法仅 Kotlin 可用.
syncFromEvent: 挂起当前协程, 监听一个事件, 并尝试从这个事件中获取一个值
nextEvent: 挂起当前协程, 直到监听到特定类型事件的广播并通过过滤器, 返回这个事件实例.
Return
监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 handler
Parameters
在 defaultCoroutineContext 的基础上, 给事件监听协程的额外的 CoroutineContext.
并发类型. 查看 ConcurrencyKind
监听优先级,优先级越高越先执行
事件处理器. 在接收到事件时会调用这个处理器. 其返回值意义参考 ListeningStatus. 其异常处理参考上文
See also
以 when
的语法 '选择' 即将到来的一条消息.
以 when
的语法 '选择' 即将到来的所有消息, 直到不满足筛选结果.
监听消息 DSL
与 subscribe 的区别是接受 eventClass 参数, 而不使用 reified
泛型. 通常推荐使用具体化类型参数.
Return
监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 handler
See also
Java API. 查看 subscribe 获取更多信息.
eventChannel.subscribe(GroupMessageEvent.class, (event) -> {
return ListeningStatus.LISTENING;
});