From ec6e23d450c299706d090066b953aea7e5df94bc Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 27 Dec 2020 15:57:27 +0800 Subject: [PATCH] Introduce EventChannel.registerListenerHost and deprecate ListenerHost.registerEvents --- .../commonMain/kotlin/event/EventChannel.kt | 39 +++++++--- .../kotlin/event/JvmMethodListeners.kt | 73 +++++++++++-------- .../src/jvmTest/kotlin/JavaApiTests.java | 3 +- 3 files changed, 75 insertions(+), 40 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt index 2f75a898c..73d82ce11 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt @@ -38,6 +38,7 @@ import kotlin.reflect.KClass * 事件通道. 事件通道是监听事件的入口. **在不同的事件通道中可以监听到不同类型的事件**. * * [GlobalEventChannel] 是最大的通道: 所有的事件都可以在 [GlobalEventChannel] 监听到. + * 通过 [Bot.eventChannel] 得到的通道只能监听到来自这个 [Bot] 的事件. * * ### 对通道的操作 * - "缩窄" 通道: 通过 [EventChannel.filter]. 例如 `filter { it is BotEvent }` 得到一个只能监听到 [BotEvent] 的事件通道. @@ -254,26 +255,28 @@ public open class EventChannel @JvmOverloads constructor( * ### 创建监听 * 调用本函数: * ``` - * subscribe { /* 会收到来自全部 Bot 的事件和与 Bot 不相关的事件 */ } + * eventChannel.subscribe { /* 会收到此通道中的所有是 E 的事件 */ } * ``` * * ### 生命周期 * * #### 通过协程作用域管理监听器 - * 本函数将会创建一个 [Job], 成为 [coroutineContext] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器: + * 本函数将会创建一个 [Job], 成为 [parentJob] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器: * ``` * val scope = CoroutineScope(SupervisorJob()) * - * eventChannel.subscribeAlways { /* ... */ } - * eventChannel.subscribeAlways { /* ... */ } + * val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel * - * scope.cancel() // 停止上文两个监听 + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * + * scope.cancel() // 停止了协程作用域, 也就取消了两个监听器 * ``` * * **注意**, 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job] * ``` * runBlocking { // this: CoroutineScope - * subscribe { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob + * eventChannel.subscribe { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob * } * // runBlocking 不会完结, 直到监听时创建的 `Listener` 被停止. * // 它可能通过 Listener.cancel() 停止, 也可能自行返回 ListeningStatus.Stopped 停止. @@ -284,7 +287,7 @@ public open class EventChannel @JvmOverloads constructor( * 或 [Listener.complete] 后结束. * * ### 子类监听 - * 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听所有类型的事件. + * 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听此通道中所有类型的事件. * * ### 异常处理 * - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常: @@ -388,8 +391,7 @@ public open class EventChannel @JvmOverloads constructor( ) /** - * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件. - * 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行. + * 订阅所有 [E] 及其子类事件. 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行. * * 可在任意时候通过 [Listener.complete] 来主动停止监听. * [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel]. @@ -421,6 +423,25 @@ public open class EventChannel @JvmOverloads constructor( // endregion + /** + * 注册 [ListenerHost] 中的所有 [EventHandler] 标注的方法到这个 [EventChannel]. + * + * @see subscribe + * @see EventHandler + * @see ListenerHost + */ + @JvmOverloads + public fun registerListenerHost( + host: ListenerHost, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + ) { + for (method in host.javaClass.declaredMethods) { + method.getAnnotation(EventHandler::class.java)?.let { + method.registerEventHandler(host, this, it, coroutineContext) + } + } + } + // region Java API /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt b/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt index c55d4c93e..134311e4e 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt @@ -15,6 +15,7 @@ package net.mamoe.mirai.event import kotlinx.coroutines.* import net.mamoe.mirai.utils.EventListenerLikeJava +import net.mamoe.mirai.utils.PlannedRemoval import net.mamoe.mirai.utils.castOrNull import java.lang.reflect.Method import kotlin.coroutines.CoroutineContext @@ -103,7 +104,8 @@ import kotlin.reflect.jvm.kotlinFunction * } * } * - * MyEvents.registerEvents() + * MyEvents.registerTo(eventChannel) + * // 或 eventChannel.registerListenerHost(MyEvents) * ``` * * @@ -143,7 +145,7 @@ import kotlin.reflect.jvm.kotlinFunction * } * } * - * Events.registerEvents(new MyEventHandlers()) + * eventChannel.registerListenerHost(new MyEventHandlers()) * ``` * * //@sample net.mamoe.mirai.event.JvmMethodEventsTest @@ -179,6 +181,8 @@ public interface ListenerHost /** * 携带一个异常处理器的 [ListenerHost]. + * + * @see registerTo * @see ListenerHost 查看更多信息 * @see EventHandler 查看更多信息 */ @@ -243,35 +247,46 @@ public class ExceptionInEventHandlerException( override val cause: Throwable ) : IllegalStateException() -/** - * 反射得到所有标注了 [EventHandler] 的函数 (Java 为方法), 并注册为事件监听器 - * - * Java 使用: `Events.registerEvents(listenerHost)` - * - * @see EventHandler 获取更多信息 - */ -@JvmOverloads -public fun T.registerEvents(coroutineContext: CoroutineContext = EmptyCoroutineContext): Unit - where T : CoroutineScope, T : ListenerHost = this.registerEvents(this, coroutineContext) /** * 反射得到所有标注了 [EventHandler] 的函数 (Java 为方法), 并注册为事件监听器 * - * Java 使用: `Events.registerEvents(coroutineScope, host)` - * * @see EventHandler 获取更多信息 */ +@JvmSynthetic +// T 通常可以是 SimpleListenerHost +public inline fun T.registerTo(eventChannel: EventChannel<*>): Unit + where T : CoroutineScope, T : ListenerHost = eventChannel.registerListenerHost(this) + + +@Deprecated( + "Use EventChannel.registerListenerHost", + ReplaceWith( + "this.globalEventChannel(coroutineContext).registerListenerHost(this)", + "net.mamoe.mirai.event.*" + ), + DeprecationLevel.ERROR +) +@PlannedRemoval("2.0-RC") +@JvmOverloads +public fun T.registerEvents(coroutineContext: CoroutineContext = EmptyCoroutineContext): Unit + where T : CoroutineScope, T : ListenerHost = + this.globalEventChannel(coroutineContext).registerListenerHost(this) + +@Deprecated( + "Use EventChannel.registerListenerHost", + ReplaceWith( + "this.globalEventChannel(coroutineContext).registerListenerHost(host)", + "net.mamoe.mirai.event.*" + ), + DeprecationLevel.ERROR +) +@PlannedRemoval("2.0-RC") @JvmOverloads public fun CoroutineScope.registerEvents( host: ListenerHost, coroutineContext: CoroutineContext = EmptyCoroutineContext -) { - for (method in host.javaClass.declaredMethods) { - method.getAnnotation(EventHandler::class.java)?.let { - method.registerEvent(host, this, it, coroutineContext) - } - } -} +): Unit = globalEventChannel(coroutineContext).registerListenerHost(host) private fun Method.isKotlinFunction(): Boolean { @@ -283,9 +298,9 @@ private fun Method.isKotlinFunction(): Boolean { } @Suppress("UNCHECKED_CAST") -private fun Method.registerEvent( +internal fun Method.registerEventHandler( owner: Any, - scope: CoroutineScope, + eventChannel: EventChannel<*>, annotation: EventHandler, coroutineContext: CoroutineContext, ): Listener { @@ -348,7 +363,7 @@ private fun Method.registerEvent( } when (kotlinFunction.returnType.classifier) { Unit::class, Nothing::class -> { - scope.globalEventChannel().subscribeAlways( + eventChannel.subscribeAlways( param[1].type.classifier as KClass, coroutineContext, annotation.concurrency, @@ -362,7 +377,7 @@ private fun Method.registerEvent( }.also { listener = it } } ListeningStatus::class -> { - scope.globalEventChannel().subscribe( + eventChannel.subscribe( param[1].type.classifier as KClass, coroutineContext, annotation.concurrency, @@ -400,17 +415,17 @@ private fun Method.registerEvent( return if (annotation.ignoreCancelled) { if (event.castOrNull()?.isCancelled != true) { withContext(Dispatchers.IO) { - this@registerEvent.invokeWithErrorReport(owner, event) + this@registerEventHandler.invokeWithErrorReport(owner, event) } } else ListeningStatus.LISTENING } else withContext(Dispatchers.IO) { - this@registerEvent.invokeWithErrorReport(owner, event) + this@registerEventHandler.invokeWithErrorReport(owner, event) } } when (this.returnType) { Void::class.java, Void.TYPE, Nothing::class.java -> { - scope.globalEventChannel().subscribeAlways( + eventChannel.subscribeAlways( paramType.kotlin as KClass, coroutineContext, annotation.concurrency, @@ -420,7 +435,7 @@ private fun Method.registerEvent( } } ListeningStatus::class.java -> { - scope.globalEventChannel().subscribe( + eventChannel.subscribe( paramType.kotlin as KClass, coroutineContext, annotation.concurrency, diff --git a/mirai-core/src/jvmTest/kotlin/JavaApiTests.java b/mirai-core/src/jvmTest/kotlin/JavaApiTests.java index 4c6ce159a..78527aa76 100644 --- a/mirai-core/src/jvmTest/kotlin/JavaApiTests.java +++ b/mirai-core/src/jvmTest/kotlin/JavaApiTests.java @@ -11,7 +11,6 @@ import kotlin.coroutines.CoroutineContext; import net.mamoe.mirai.Bot; import net.mamoe.mirai.BotFactory; import net.mamoe.mirai.event.EventHandler; -import net.mamoe.mirai.event.Events; import net.mamoe.mirai.event.ListeningStatus; import net.mamoe.mirai.event.SimpleListenerHost; import net.mamoe.mirai.event.events.GroupMessageEvent; @@ -57,6 +56,6 @@ public class JavaApiTests { } }; - Events.registerEvents(slh); + bot.getEventChannel().registerListenerHost(slh); } }