mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-03 15:10:14 +08:00
Introduce EventChannel.registerListenerHost and deprecate ListenerHost.registerEvents
This commit is contained in:
parent
de4bbafbd4
commit
ec6e23d450
@ -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<out BaseEvent : Event> @JvmOverloads constructor(
|
||||
* ### 创建监听
|
||||
* 调用本函数:
|
||||
* ```
|
||||
* subscribe<Event> { /* 会收到来自全部 Bot 的事件和与 Bot 不相关的事件 */ }
|
||||
* eventChannel.subscribe<E> { /* 会收到此通道中的所有是 E 的事件 */ }
|
||||
* ```
|
||||
*
|
||||
* ### 生命周期
|
||||
*
|
||||
* #### 通过协程作用域管理监听器
|
||||
* 本函数将会创建一个 [Job], 成为 [coroutineContext] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器:
|
||||
* 本函数将会创建一个 [Job], 成为 [parentJob] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器:
|
||||
* ```
|
||||
* val scope = CoroutineScope(SupervisorJob())
|
||||
*
|
||||
* eventChannel.subscribeAlways<MemberJoinEvent> { /* ... */ }
|
||||
* eventChannel.subscribeAlways<MemberMuteEvent> { /* ... */ }
|
||||
* val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel
|
||||
*
|
||||
* scope.cancel() // 停止上文两个监听
|
||||
* scopedChannel.subscribeAlways<MemberJoinEvent> { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务
|
||||
* scopedChannel.subscribeAlways<MemberMuteEvent> { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务
|
||||
*
|
||||
* scope.cancel() // 停止了协程作用域, 也就取消了两个监听器
|
||||
* ```
|
||||
*
|
||||
* **注意**, 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job]
|
||||
* ```
|
||||
* runBlocking { // this: CoroutineScope
|
||||
* subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
|
||||
* eventChannel.subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
|
||||
* }
|
||||
* // runBlocking 不会完结, 直到监听时创建的 `Listener` 被停止.
|
||||
* // 它可能通过 Listener.cancel() 停止, 也可能自行返回 ListeningStatus.Stopped 停止.
|
||||
@ -284,7 +287,7 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads constructor(
|
||||
* 或 [Listener.complete] 后结束.
|
||||
*
|
||||
* ### 子类监听
|
||||
* 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听所有类型的事件.
|
||||
* 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听此通道中所有类型的事件.
|
||||
*
|
||||
* ### 异常处理
|
||||
* - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常:
|
||||
@ -388,8 +391,7 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads constructor(
|
||||
)
|
||||
|
||||
/**
|
||||
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
|
||||
* 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行.
|
||||
* 订阅所有 [E] 及其子类事件. 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行.
|
||||
*
|
||||
* 可在任意时候通过 [Listener.complete] 来主动停止监听.
|
||||
* [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel].
|
||||
@ -421,6 +423,25 @@ public open class EventChannel<out BaseEvent : Event> @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
|
||||
|
||||
/**
|
||||
|
@ -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> 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> 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> 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<Event> {
|
||||
@ -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<out Event>,
|
||||
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<out Event>,
|
||||
coroutineContext,
|
||||
annotation.concurrency,
|
||||
@ -400,17 +415,17 @@ private fun Method.registerEvent(
|
||||
return if (annotation.ignoreCancelled) {
|
||||
if (event.castOrNull<CancellableEvent>()?.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<out Event>,
|
||||
coroutineContext,
|
||||
annotation.concurrency,
|
||||
@ -420,7 +435,7 @@ private fun Method.registerEvent(
|
||||
}
|
||||
}
|
||||
ListeningStatus::class.java -> {
|
||||
scope.globalEventChannel().subscribe(
|
||||
eventChannel.subscribe(
|
||||
paramType.kotlin as KClass<out Event>,
|
||||
coroutineContext,
|
||||
annotation.concurrency,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user