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] 监听到.
|
* [GlobalEventChannel] 是最大的通道: 所有的事件都可以在 [GlobalEventChannel] 监听到.
|
||||||
|
* 通过 [Bot.eventChannel] 得到的通道只能监听到来自这个 [Bot] 的事件.
|
||||||
*
|
*
|
||||||
* ### 对通道的操作
|
* ### 对通道的操作
|
||||||
* - "缩窄" 通道: 通过 [EventChannel.filter]. 例如 `filter { it is BotEvent }` 得到一个只能监听到 [BotEvent] 的事件通道.
|
* - "缩窄" 通道: 通过 [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())
|
* val scope = CoroutineScope(SupervisorJob())
|
||||||
*
|
*
|
||||||
* eventChannel.subscribeAlways<MemberJoinEvent> { /* ... */ }
|
* val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel
|
||||||
* eventChannel.subscribeAlways<MemberMuteEvent> { /* ... */ }
|
|
||||||
*
|
*
|
||||||
* scope.cancel() // 停止上文两个监听
|
* scopedChannel.subscribeAlways<MemberJoinEvent> { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务
|
||||||
|
* scopedChannel.subscribeAlways<MemberMuteEvent> { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务
|
||||||
|
*
|
||||||
|
* scope.cancel() // 停止了协程作用域, 也就取消了两个监听器
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* **注意**, 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job]
|
* **注意**, 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job]
|
||||||
* ```
|
* ```
|
||||||
* runBlocking { // this: CoroutineScope
|
* runBlocking { // this: CoroutineScope
|
||||||
* subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
|
* eventChannel.subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
|
||||||
* }
|
* }
|
||||||
* // runBlocking 不会完结, 直到监听时创建的 `Listener` 被停止.
|
* // runBlocking 不会完结, 直到监听时创建的 `Listener` 被停止.
|
||||||
* // 它可能通过 Listener.cancel() 停止, 也可能自行返回 ListeningStatus.Stopped 停止.
|
* // 它可能通过 Listener.cancel() 停止, 也可能自行返回 ListeningStatus.Stopped 停止.
|
||||||
@ -284,7 +287,7 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads constructor(
|
|||||||
* 或 [Listener.complete] 后结束.
|
* 或 [Listener.complete] 后结束.
|
||||||
*
|
*
|
||||||
* ### 子类监听
|
* ### 子类监听
|
||||||
* 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听所有类型的事件.
|
* 监听父类事件, 也会同时监听其子类. 因此监听 [Event] 即可监听此通道中所有类型的事件.
|
||||||
*
|
*
|
||||||
* ### 异常处理
|
* ### 异常处理
|
||||||
* - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常:
|
* - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常:
|
||||||
@ -388,8 +391,7 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
|
* 订阅所有 [E] 及其子类事件. 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行.
|
||||||
* 仅在第一次 [事件广播][Event.broadcast] 时, [handler] 会被执行.
|
|
||||||
*
|
*
|
||||||
* 可在任意时候通过 [Listener.complete] 来主动停止监听.
|
* 可在任意时候通过 [Listener.complete] 来主动停止监听.
|
||||||
* [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel].
|
* [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel].
|
||||||
@ -421,6 +423,25 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads constructor(
|
|||||||
|
|
||||||
// endregion
|
// 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
|
// region Java API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@ package net.mamoe.mirai.event
|
|||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.utils.EventListenerLikeJava
|
import net.mamoe.mirai.utils.EventListenerLikeJava
|
||||||
|
import net.mamoe.mirai.utils.PlannedRemoval
|
||||||
import net.mamoe.mirai.utils.castOrNull
|
import net.mamoe.mirai.utils.castOrNull
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import kotlin.coroutines.CoroutineContext
|
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
|
* //@sample net.mamoe.mirai.event.JvmMethodEventsTest
|
||||||
@ -179,6 +181,8 @@ public interface ListenerHost
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 携带一个异常处理器的 [ListenerHost].
|
* 携带一个异常处理器的 [ListenerHost].
|
||||||
|
*
|
||||||
|
* @see registerTo
|
||||||
* @see ListenerHost 查看更多信息
|
* @see ListenerHost 查看更多信息
|
||||||
* @see EventHandler 查看更多信息
|
* @see EventHandler 查看更多信息
|
||||||
*/
|
*/
|
||||||
@ -243,35 +247,46 @@ public class ExceptionInEventHandlerException(
|
|||||||
override val cause: Throwable
|
override val cause: Throwable
|
||||||
) : IllegalStateException()
|
) : 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 为方法), 并注册为事件监听器
|
* 反射得到所有标注了 [EventHandler] 的函数 (Java 为方法), 并注册为事件监听器
|
||||||
*
|
*
|
||||||
* Java 使用: `Events.registerEvents(coroutineScope, host)`
|
|
||||||
*
|
|
||||||
* @see EventHandler 获取更多信息
|
* @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
|
@JvmOverloads
|
||||||
public fun CoroutineScope.registerEvents(
|
public fun CoroutineScope.registerEvents(
|
||||||
host: ListenerHost,
|
host: ListenerHost,
|
||||||
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
) {
|
): Unit = globalEventChannel(coroutineContext).registerListenerHost(host)
|
||||||
for (method in host.javaClass.declaredMethods) {
|
|
||||||
method.getAnnotation(EventHandler::class.java)?.let {
|
|
||||||
method.registerEvent(host, this, it, coroutineContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Method.isKotlinFunction(): Boolean {
|
private fun Method.isKotlinFunction(): Boolean {
|
||||||
|
|
||||||
@ -283,9 +298,9 @@ private fun Method.isKotlinFunction(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun Method.registerEvent(
|
internal fun Method.registerEventHandler(
|
||||||
owner: Any,
|
owner: Any,
|
||||||
scope: CoroutineScope,
|
eventChannel: EventChannel<*>,
|
||||||
annotation: EventHandler,
|
annotation: EventHandler,
|
||||||
coroutineContext: CoroutineContext,
|
coroutineContext: CoroutineContext,
|
||||||
): Listener<Event> {
|
): Listener<Event> {
|
||||||
@ -348,7 +363,7 @@ private fun Method.registerEvent(
|
|||||||
}
|
}
|
||||||
when (kotlinFunction.returnType.classifier) {
|
when (kotlinFunction.returnType.classifier) {
|
||||||
Unit::class, Nothing::class -> {
|
Unit::class, Nothing::class -> {
|
||||||
scope.globalEventChannel().subscribeAlways(
|
eventChannel.subscribeAlways(
|
||||||
param[1].type.classifier as KClass<out Event>,
|
param[1].type.classifier as KClass<out Event>,
|
||||||
coroutineContext,
|
coroutineContext,
|
||||||
annotation.concurrency,
|
annotation.concurrency,
|
||||||
@ -362,7 +377,7 @@ private fun Method.registerEvent(
|
|||||||
}.also { listener = it }
|
}.also { listener = it }
|
||||||
}
|
}
|
||||||
ListeningStatus::class -> {
|
ListeningStatus::class -> {
|
||||||
scope.globalEventChannel().subscribe(
|
eventChannel.subscribe(
|
||||||
param[1].type.classifier as KClass<out Event>,
|
param[1].type.classifier as KClass<out Event>,
|
||||||
coroutineContext,
|
coroutineContext,
|
||||||
annotation.concurrency,
|
annotation.concurrency,
|
||||||
@ -400,17 +415,17 @@ private fun Method.registerEvent(
|
|||||||
return if (annotation.ignoreCancelled) {
|
return if (annotation.ignoreCancelled) {
|
||||||
if (event.castOrNull<CancellableEvent>()?.isCancelled != true) {
|
if (event.castOrNull<CancellableEvent>()?.isCancelled != true) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
this@registerEvent.invokeWithErrorReport(owner, event)
|
this@registerEventHandler.invokeWithErrorReport(owner, event)
|
||||||
}
|
}
|
||||||
} else ListeningStatus.LISTENING
|
} else ListeningStatus.LISTENING
|
||||||
} else withContext(Dispatchers.IO) {
|
} else withContext(Dispatchers.IO) {
|
||||||
this@registerEvent.invokeWithErrorReport(owner, event)
|
this@registerEventHandler.invokeWithErrorReport(owner, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (this.returnType) {
|
when (this.returnType) {
|
||||||
Void::class.java, Void.TYPE, Nothing::class.java -> {
|
Void::class.java, Void.TYPE, Nothing::class.java -> {
|
||||||
scope.globalEventChannel().subscribeAlways(
|
eventChannel.subscribeAlways(
|
||||||
paramType.kotlin as KClass<out Event>,
|
paramType.kotlin as KClass<out Event>,
|
||||||
coroutineContext,
|
coroutineContext,
|
||||||
annotation.concurrency,
|
annotation.concurrency,
|
||||||
@ -420,7 +435,7 @@ private fun Method.registerEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ListeningStatus::class.java -> {
|
ListeningStatus::class.java -> {
|
||||||
scope.globalEventChannel().subscribe(
|
eventChannel.subscribe(
|
||||||
paramType.kotlin as KClass<out Event>,
|
paramType.kotlin as KClass<out Event>,
|
||||||
coroutineContext,
|
coroutineContext,
|
||||||
annotation.concurrency,
|
annotation.concurrency,
|
||||||
|
@ -11,7 +11,6 @@ import kotlin.coroutines.CoroutineContext;
|
|||||||
import net.mamoe.mirai.Bot;
|
import net.mamoe.mirai.Bot;
|
||||||
import net.mamoe.mirai.BotFactory;
|
import net.mamoe.mirai.BotFactory;
|
||||||
import net.mamoe.mirai.event.EventHandler;
|
import net.mamoe.mirai.event.EventHandler;
|
||||||
import net.mamoe.mirai.event.Events;
|
|
||||||
import net.mamoe.mirai.event.ListeningStatus;
|
import net.mamoe.mirai.event.ListeningStatus;
|
||||||
import net.mamoe.mirai.event.SimpleListenerHost;
|
import net.mamoe.mirai.event.SimpleListenerHost;
|
||||||
import net.mamoe.mirai.event.events.GroupMessageEvent;
|
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