From 7f9b500e226a02d32126af2703de44aa21f2158e Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 8 Jan 2021 10:37:20 +0800 Subject: [PATCH] [Review] EventChannel: Update docs --- .../commonMain/kotlin/event/EventChannel.kt | 70 +++++++++---------- .../kotlin/event/subscribeMessages.kt | 6 ++ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt index 3ae779a7d..4d32e567a 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt @@ -17,6 +17,7 @@ package net.mamoe.mirai.event import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.sync.Mutex import net.mamoe.mirai.Bot import net.mamoe.mirai.event.Listener.ConcurrencyKind.CONCURRENT import net.mamoe.mirai.event.Listener.ConcurrencyKind.LOCKED @@ -25,7 +26,6 @@ import net.mamoe.mirai.internal.event.GlobalEventListeners import net.mamoe.mirai.internal.event.Handler import net.mamoe.mirai.internal.event.ListenerRegistry import net.mamoe.mirai.internal.event.registerEventHandler -import net.mamoe.mirai.utils.JavaFriendlyAPI import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiLogger import java.util.function.Consumer @@ -52,12 +52,12 @@ import kotlin.reflect.KClass * - [EventChannel.subscribeOnce] 创建一个只监听单次的事件监听器. * * ### 获取事件通道 - * - [GlobalEventChannel] - * - [Bot.eventChannel] + * - 全局事件通道: [GlobalEventChannel] + * - [BotEvent] 通道: [Bot.eventChannel] * * @see subscribe */ -public open class EventChannel @JvmOverloads constructor( +public open class EventChannel @JvmOverloads internal constructor( public val baseEventClass: KClass, /** * 此事件通道的默认 [CoroutineScope.coroutineContext]. 将会被添加给所有注册的事件监听器. @@ -100,7 +100,7 @@ public open class EventChannel @JvmOverloads constructor( * * 若 [filter] 返回 `true`, 该事件将会被传给监听器. 否则将会被忽略, **监听器继续监听**. * - * ### 线性顺序 + * ## 线性顺序 * 多个 [filter] 的处理是线性且有顺序的. 若一个 [filter] 已经返回了 `false` (代表忽略这个事件), 则会立即忽略, 而不会传递给后续过滤器. * * 示例: @@ -116,19 +116,19 @@ public open class EventChannel @JvmOverloads constructor( * } * ``` * - * ### 过滤器挂起 + * ## 过滤器挂起 * [filter] 允许挂起协程. **过滤器的挂起将被认为是事件监听器的挂起**. * * 过滤器挂起是否会影响事件处理, * 取决于 [subscribe] 时的 [Listener.ConcurrencyKind] 和 [Listener.EventPriority]. * - * ### 过滤器异常处理 + * ## 过滤器异常处理 * 若 [filter] 抛出异常, 将被包装为 [ExceptionInEventChannelFilterException] 并重新抛出. * * @see filterIsInstance 过滤指定类型的事件 */ @JvmSynthetic - public fun filter(filter: suspend (event: @UnsafeVariance BaseEvent) -> Boolean): EventChannel { + public fun filter(filter: suspend (event: BaseEvent) -> Boolean): EventChannel { return object : EventChannel(baseEventClass, defaultCoroutineContext) { private inline val innerThis get() = this @@ -253,15 +253,15 @@ public open class EventChannel @JvmOverloads constructor( * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行. * * - * ### 创建监听 + * ## 创建监听 * 调用本函数: * ``` * eventChannel.subscribe { /* 会收到此通道中的所有是 E 的事件 */ } * ``` * - * ### 生命周期 + * ## 生命周期 * - * #### 通过协程作用域管理监听器 + * ### 通过协程作用域管理监听器 * 本函数将会创建一个 [Job], 成为 [parentJob] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器: * ``` * val scope = CoroutineScope(SupervisorJob()) @@ -274,34 +274,40 @@ public open class EventChannel @JvmOverloads constructor( * scope.cancel() // 停止了协程作用域, 也就取消了两个监听器 * ``` * - * **注意**, 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job] - * ``` - * runBlocking { // this: CoroutineScope - * eventChannel.subscribe { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob - * } - * // runBlocking 不会完结, 直到监听时创建的 `Listener` 被停止. - * // 它可能通过 Listener.cancel() 停止, 也可能自行返回 ListeningStatus.Stopped 停止. - * ``` + * 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [parentJob] 或 [parentScope] 的一个 [子任务][Job] * - * #### 在监听器内部停止后续监听 - * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听. - * 或 [Listener.complete] 后结束. + * ### 停止监听 + * 如果 [handler] 返回 [ListeningStatus.STOPPED] 监听器将被停止. * - * ### 子类监听 - * 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听此通道中所有类型的事件. + * 也可以通过 [subscribe] 返回值 [Listener] 的 [Listener.complete] * - * ### 异常处理 + * ## 监听器调度 + * 监听器会被创建一个协程任务, 语义上在 [parentScope] 下运行. + * 通过 Kotlin [默认协程调度器][Dispatchers.Default] 在固定的全局共享线程池里执行, 除非有 [coroutineContext] 指定. + * + * 默认在 [handler] 中不能处理阻塞任务. 阻塞任务将会阻塞一个 Kotlin 全局协程调度线程并可能导致严重问题. + * 请通过 `withContext(Dispatchers.IO) { }` 等方法执行阻塞工作. + * + * ## 异常处理 * - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常: * 1. 参数 [coroutineContext] * 2. [EventChannel.defaultCoroutineContext] * 3. [Event.broadcast] 调用者的 [coroutineContext] * 4. 若事件为 [BotEvent], 则从 [BotEvent.bot] 获取到 [Bot], 进而在 [Bot.coroutineContext] 中寻找 * 5. 若以上四个步骤均无法获取 [CoroutineExceptionHandler], 则使用 [MiraiLogger.Companion] 通过日志记录. 但这种情况理论上不应发生. - * - 事件处理时抛出异常不会停止监听器. - * - 建议在事件处理中 (即 [handler] 里) 处理异常, - * 或在参数 [coroutineContext] 中添加 [CoroutineExceptionHandler]. * * + * 事件处理时抛出异常不会停止监听器. + * + * 建议在事件处理中 (即 [handler] 里) 处理异常, + * 或在参数 [coroutineContext] 中添加 [CoroutineExceptionHandler], 或通过 [EventChannel.exceptionHandler]. + * + * ## 并发安全性 + * 基于 [concurrency] 参数, 事件监听器可以被允许并行执行. + * + * - 若 [concurrency] 为 [Listener.ConcurrencyKind.CONCURRENT], [handler] 可能被并行调用, 需要保证并发安全. + * - 若 [concurrency] 为 [Listener.ConcurrencyKind.LOCKED], [handler] 会被 [Mutex] 限制. + * * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext]. * @param concurrency 并发类型. 查看 [Listener.ConcurrencyKind] * @param priority 监听优先级,优先级越高越先执行 @@ -321,9 +327,6 @@ public open class EventChannel @JvmOverloads constructor( * @see subscribeOnce 只监听一次 * * @see subscribeMessages 监听消息 DSL - * @see subscribeGroupMessages 监听群消息 DSL - * @see subscribeFriendMessages 监听好友消息 DSL - * @see subscribeTempMessages 监听临时会话消息 DSL */ @JvmSynthetic public inline fun subscribe( @@ -334,7 +337,7 @@ public open class EventChannel @JvmOverloads constructor( ): Listener = subscribe(E::class, coroutineContext, concurrency, priority, handler) /** - * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型 + * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型. 通常推荐使用具体化类型参数. * * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] * @see subscribe @@ -457,7 +460,6 @@ public open class EventChannel @JvmOverloads constructor( * @see subscribe * @see subscribeAlways */ - @JavaFriendlyAPI @JvmOverloads @LowPriorityInOverloadResolution public fun subscribeAlways( @@ -488,7 +490,6 @@ public open class EventChannel @JvmOverloads constructor( * * @see subscribe */ - @JavaFriendlyAPI @JvmOverloads @LowPriorityInOverloadResolution public fun subscribe( @@ -517,7 +518,6 @@ public open class EventChannel @JvmOverloads constructor( * @see subscribe * @see subscribeOnce */ - @JavaFriendlyAPI @JvmOverloads @LowPriorityInOverloadResolution public fun subscribeOnce( diff --git a/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt b/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt index 02d41eccf..9ff7e9954 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt @@ -66,6 +66,12 @@ public typealias MessageEventSubscribersBuilder = MessageSubscribersBuilder EventChannel<*>.subscribeMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext,