A powerful in-process event dispatcher based on Kotlin and Coroutines.

Overview

KEvent

Maven Central Kotlin 1.5.31 JDK Apache License 2.0 Awesome Kotlin Badge

A powerful in-process event dispatcher based on Kotlin and Coroutines.

Feature List

  • Implement publish–subscribe pattern

  • Tiny (52.8kb jar) and super-fast (no reflection)

  • Usable in plenty scenarios: plain kotlin, server side, android, javafx, swing

  • Use Enum as event type, so you don't have to create numerous event classes

  • Support 3 event dispatch modes with 3 subscriber thread modes

    DispatchMode\ThreadMode POSTING BACKGROUND UI
    POSTING × ×
    SEQUENTIAL ×
    CONCURRENT × ×
  • Support a bunch of advanced features:

    • event blocking
    • event dispatch cancellation
    • sticky events
    • subscriber management
    • subscriber priority
    • subscriber tag
    • subscribe multiple event types with same subscriber
    • multiple ways to subscribe and unsubscribe
    • provide a helpful subscriber interface
  • Thread safe

  • Fully tested

Download

Gradle Kotlin DSL

implementation("org.rationalityfrontline:kevent:2.1.0")

Maven

<dependency>
    <groupId>org.rationalityfrontline</groupId>
    <artifactId>kevent</artifactId>
    <version>2.1.0</version>
</dependency>

Usage

enum class EventTypes {
    UNIT_EVENT,
    STRING_EVENT,
}

private class ExampleSubscriber : KEventSubscriber {
    fun registerSubscribers() {
        subscribe<Unit>(EventTypes.UNIT_EVENT) { event ->
            println("${"ExampleSubscriber.lambda".padEnd(35)}: $event")
        }
        subscribe(EventTypes.STRING_EVENT, ::onStringEvent)
        subscribeMultiple(
            listOf(
                EventTypes.UNIT_EVENT,
                EventTypes.STRING_EVENT,
            ), ::onAnyEvent
        )
    }

    fun unregisterSubscribers() {
        unsubscribeAll()
    }

    private fun onStringEvent(event: Event<String>) {
        println("${"ExampleSubscriber.onStringEvent".padEnd(35)}: $event")
    }

    private fun onAnyEvent(event: Event<Any>) {
        try {
            when (event.type) {
                EventTypes.UNIT_EVENT -> event.data as Unit
                EventTypes.STRING_EVENT -> event.data as String
            }
        } catch (e: ClassCastException) {
            println("Different event data types might come with same event type: ${e.message}")
        }
        println("${"ExampleSubscriber.onAnyEvent".padEnd(35)}: $event")
    }
}

private fun topLevelOnStringEvent(event: Event<String>) {
    println("${"topLevelOnStringEvent".padEnd(35)}: $event")
}

fun main() {
    KEVENT.subscribe<Unit>(EventTypes.UNIT_EVENT, tag = "main") { event ->
        println("${"main.lambda".padEnd(35)}: $event")
    }
    KEVENT.subscribe(EventTypes.STRING_EVENT, ::topLevelOnStringEvent)

    val subscriber = ExampleSubscriber()
    subscriber.registerSubscribers()

    KEVENT.post(EventTypes.UNIT_EVENT, Unit)
    KEVENT.post(EventTypes.STRING_EVENT, "KEvent is awesome!")
    KEVENT.post(EventTypes.STRING_EVENT, 42)

    subscriber.unregisterSubscribers()

    KEVENT.removeSubscribersByTag("main")
    KEVENT.unsubscribe(EventTypes.STRING_EVENT, ::topLevelOnStringEvent)
}

Running the code above will produce the following outputs:

main.lambda                        : Event(type=UNIT_EVENT, data=kotlin.Unit, dispatchMode=POSTING, isSticky=false)
ExampleSubscriber.lambda           : Event(type=UNIT_EVENT, data=kotlin.Unit, dispatchMode=POSTING, isSticky=false)
ExampleSubscriber.onAnyEvent       : Event(type=UNIT_EVENT, data=kotlin.Unit, dispatchMode=POSTING, isSticky=false)
topLevelOnStringEvent              : Event(type=STRING_EVENT, data=KEvent is awesome!, dispatchMode=POSTING, isSticky=false)
ExampleSubscriber.onStringEvent    : Event(type=STRING_EVENT, data=KEvent is awesome!, dispatchMode=POSTING, isSticky=false)
ExampleSubscriber.onAnyEvent       : Event(type=STRING_EVENT, data=KEvent is awesome!, dispatchMode=POSTING, isSticky=false)
topLevelOnStringEvent              : Event(type=STRING_EVENT, data=42, dispatchMode=POSTING, isSticky=false)
ExampleSubscriber.onStringEvent    : Event(type=STRING_EVENT, data=42, dispatchMode=POSTING, isSticky=false)
Different event data types might come with same event type: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
ExampleSubscriber.onAnyEvent       : Event(type=STRING_EVENT, data=42, dispatchMode=POSTING, isSticky=false)

For advanced features, please refer to the corresponding test specifications:

Performance

There is a sample benchmark code in the repository, you can clone this repository and run the benchmark on your own machine. Here is the benchmark results on my machine:

Conditions\AvgCallTime(ms)\ThreadMode POSTING SEQUENTIAL CONCURRENT
event-1; subs-10000; tc-false; st-false 4.6920002E-5 0.00142243 8.7184E-4
event-1; subs-10000; tc-true; st-false 10.446612 10.578969 1.3352561
event-10000; subs-1; tc-false; st-false 0.00128533 0.00481413 0.0026817601
event-10000; subs-1; tc-true; st-false 10.942825 1.3770571 1.3426003
event-1000; subs-10000; tc-false; st-false 2.238846E-5 3.071547E-4 6.2108616E-4
event-1; subs-10000; tc-false; st-true 6.9659E-4
event-1; subs-10000; tc-true; st-true 1.3062543
event-10000; subs-1; tc-false; st-true 0.0034271
event-10000; subs-1; tc-true; st-true 1.3126742
event-1000; subs-10000; tc-false; st-true 6.246506E-4

event = event num
subs = subscriber num
tc = if subscribers are time-consuming (sleep 10ms in the above benchmark)
st = if the event is sticky (and subscribers are added after the event was posted)

[Machine Info]
CPU: Intel(R) Core(TM) i7-4710MQ @ 2.50GHz (4C8T)
Memory: 8 + 8 = 16GB, DDR3, 1600MHz
OS: Window 10 Enterprise 64 bit version 1909
JDK: OpenJDK 17+35-2724 64 bit

License

KEvent is released under the Apache 2.0 license.

Copyright 2020-2021 RationalityFrontline

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
You might also like...
🎲 A powerful and simple-to-use guilded wrapper made in Kotlin.

🎲 deck [WIP] Deck is a powerful yet simple-to-use guilded wrapper made entirely in Kotlin with support to multiplatform. Implementating In case you'r

A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisation and okio

Store A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisatio

Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

A lightweight, simple, smart and powerful Android routing library.

RxRouter Read this in other languages: 中文, English A lightweight, simple, smart and powerful Android routing library. Getting started Setting up the d

Runtime Mobile Security (RMS) 📱🔥  - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) 📱 🔥 by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

This is powerful android framework
This is powerful android framework

FlairFramework This is an android framework for build complex application with different architectures (MVC ready/MVP/MVVM/MVI ets). It's create on to

A powerful tool (iOS & Android) that focuses on mobile operation behavior!
A powerful tool (iOS & Android) that focuses on mobile operation behavior!

DiDiPrism,中文名:小桔棱镜,是一款专注于移动端操作行为的工具,涵盖APP操作回放、操作检测、以及数据可视化能力。我们在整个方案的实现过程中沉淀出了一套技术框架,希望可以逐步开源出来帮助更多人,同时也希望棱镜在大家的合力下能够更快的成长。 它有哪些亮点? 零入侵 业务代码无需任何适配。 高可

A powerful Minecraft Server Software coming from the future

Mirai A powerful Minecraft Server Software coming from the future Mirai is ❗ under heavy development ❗ and contributions are welcome! Features 30% fas

Kauth - Open-source powerful minecraft authorization plugin
Kauth - Open-source powerful minecraft authorization plugin

KAuth KAuth is a minecraft plugin for offline-mode authorization on your server.

Releases(v2.2.0)
Owner
WeChat: OneCivilization
null
Skip the process of setting up a new android project!

Android-Project-Template Skip the process of setting up an android app by using this template. Prerequisites Have an idea about: Clean Architecture De

Kibet 7 Aug 1, 2022
Recycler-coroutines - RecyclerView Auto Add Data Using Coroutines

Sample RecyclerView Auto Add With Coroutine Colaborator Very open to anyone, I'l

Faisal Amir 8 Dec 1, 2022
Clean MVVM with eliminating the usage of context from view models by introducing hilt for DI and sealed classes for displaying Errors in views using shared flows (one time event), and Stateflow for data

Clean ViewModel with Sealed Classes Following are the purposes of this repo Showing how you can remove the need of context in ViewModels. I. By using

Kashif Mehmood 22 Oct 26, 2022
A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Nek.12 4 Oct 31, 2022
A framework for writing composable parsers based on Kotlin Coroutines.

Parsus A framework for writing composable parsers based on Kotlin Coroutines. val booleanGrammar = object : Grammar<BooleanExpression>() { val ws

Aleksei Semin 28 Nov 1, 2022
Extension functions over Android's callback-based APIs which allows writing them in a sequential way within coroutines or observe multiple callbacks through kotlin flow.

callback-ktx A lightweight Android library that wraps Android's callback-based APIs into suspending extension functions which allow writing them in a

Sagar Viradiya 171 Oct 31, 2022
🪐 Modern Android development with Hilt, Coroutines, Flow, JetPack(ViewModel) based on MVVM architecture.

Ceres ?? Modern Android development with Hilt, Coroutines, Flow, JetPack(ViewModel) based on MVVM architecture. Download Gradle Add the dependency bel

Teodor G. 21 Jan 11, 2023
The most complete and powerful data-binding library and persistence infra for Kotlin 1.3, Android & Splitties Views DSL, JavaFX & TornadoFX, JSON, JDBC & SQLite, SharedPreferences.

Lychee (ex. reactive-properties) Lychee is a library to rule all the data. ToC Approach to declaring data Properties Other data-binding libraries Prop

Mike 112 Dec 9, 2022
⏰ A powerful and simple-to-use guilded wrapper made in Kotlin.

⏰ guilded-kt [WIP] A powerful yet simple-to-use guilded wrapper made entirely in Kotlin with supporting multiplatform. Take a look at an example of th

Gabriel 13 Jul 30, 2022