KEvent is a high-performance, lightweight EventBus library for Kotlin. It is designed to be reflection-free, thread-safe, and deeply integrated with Kotlin Coroutines.
- No Reflection: Extremely fast execution by avoiding expensive reflection lookups.
- Simple DSL: Intuitive API for building the bus and registering listeners.
- Priority System: Fine-grained control over listener execution order (from
HIGHESTtoMONITOR). - Weak Subscriptions: Prevent memory leaks by allowing listeners to be garbage collected automatically.
- Hierarchical Lookups: Optionally handle events based on class inheritance.
- Coroutine Support: Native support for
suspendfunctions and async posting. - Execution Metrics: Track how long each listener takes to process an event.
- Cancellable Events: Built-in support for event cancellation logic.
Add the repository and dependency to your build.gradle.kts:
repositories {
maven("https://repo.nekroplex.com/releases")
}
dependencies {
implementation("gg.aquatic:KEvent:1.0.4")
}val bus = eventBusBuilder {
scope = null // Uses runBlocking { } for sync posts. Set a CoroutineScope for async support.
exceptionHandler = { sub, event, ex -> println("Error in ${sub.name}: ${ex.message}") }
hierarchical = true // Match event subclasses (default: true)
}class UserLoginEvent(val username: String)
class CancellableAction(override var cancelled: Boolean = false) : Cancellable// Basic subscription
bus.subscribe<UserLoginEvent> { event ->
println("Welcome, ${event.username}!")
}
// Priority & Cancellation handling
bus.subscribe<CancellableAction>(
priority = EventPriority.HIGHEST,
ignoreCancelled = false // Won't run if the event was already cancelled
) { event ->
// Handle logic...
}
// Weak subscription (Prevent memory leaks in temporary objects)
bus.subscribeWeak<UserLoginEvent> { println("Checking login...") }// Blocking post (returns Deferred)
bus.post(UserLoginEvent("Aquatic"))
// Suspend post (preferred inside coroutines)
val result = bus.postSuspend(UserLoginEvent("Aquatic"))
// Check metrics
result.executionTimes.forEach { (sub, time) ->
println("Listener ${sub.name} took ${time}ms")
}To get the most out of KEvent, keep these tips in mind:
When building the EventBus, if you leave scope = null, the bus will use runBlocking { } for synchronous calls.
Warning: Using
runBlockingfreezes the current thread until all listeners finish. For high-concurrency environments, it is strongly recommended to provide a properCoroutineScope.
If you are already inside a suspending function or a coroutine, always prefer bus.postSuspend(event).
Why? bus.post(event) returns a Deferred and involves extra overhead for job management. postSuspend is a direct call that avoids this overhead and integrates perfectly with your existing coroutine context.
If you have a huge number of subscriptions and don't need inheritance matching (e.g., you only ever post exact classes), setting hierarchical = false in the builder can provide a small performance boost by skipping isAssignableFrom checks.
Use EventPriority.MONITOR for listeners that only need to log or observe the final state of an event without modifying it.
Got questions, need help, or want to showcase what you've built with KEvent? Join our community!
- Discord: Join the Aquatic Development Discord
- Issues: Open a ticket on GitHub for bugs or feature requests.