The event bus design pattern is very popular and advantageous in programming. The publishers trigger events and subscribers can listen to those events from anywhere. It reduces unnecessary coupling. With the arrival of observers and other frameworks, this design pattern is in less use nowadays. To achieve broadcasting of events in Android, we already had the following options:
- RxBus
- Local Broadcast
- Third parties i.e. green robot etc.
While Kotlin is gaining popularity, language is providing so many cutting-edge frameworks. Flow is one of them. Kotlin released a stable version of StateFlow and SharedFlow with the language's 1.4 release. We can achieve the EventBus design pattern using SharedFlow and leverage the benefits of language and framework.
Implementation
You can achieve event bus using shared flow using following easy steps:
Step 1:
Create a generic event bus class as follows:
object EventBus {
private val _events = MutableSharedFlow<Any>()
val events = _events.asSharedFlow()
suspend fun publish(event: Any) {
_events.emit(event)
}
suspend inline fun <reified T> subscribe(crossinline onEvent: (T) -> Unit) {
events.filterIsInstance<T>()
.collectLatest { event ->
coroutineContext.ensureActive()
onEvent(event)
}
}
}
Here, the publish
method posts an event passed as a parameter. Subscribe method collects those events. It does some utility work i.e. filtering correct event, ensuring coroutine context is active or not then providing event on the onEvent()
method. Moreover, you can collect public events
shared flow variable to apply custom logic if needed.
Step 2:
Create an event class as follows:
data class LoginEvent(
val userId: String,
val userName: String
)
Ideally, your event classes would be data classes as in any event, you would require some data. But it could be any kind of class.
Step 3:
Now use those methods to publish and subscribe to events. Creating a separate class would be preferred as per module or feature. One example is as follows:
class LoginEventHandler {
suspend fun postLoginEvent(loginEvent: LoginEvent) {
EventBus.publish(loginEvent)
}
fun subscribeLoginEvent(lifecycleOwner: LifecycleOwner) {
lifecycleOwner.lifecycleScope.launch {
EventBus.subscribe<LoginEvent> { loginEvent ->
Log.d("LoginEventHandler", "${loginEvent.userName} logged-in successfully")
}
}
}
}
And that's it!!!
Conclusion
Event bus has been a debatable topic among developers. Some say it should not be followed because it could act as a single point of failure. Sometimes, it could be hard to debug what is the source of the event.
However, it is widely accepted and useful for many use cases i.e. updating UI on internet connectivity changes, refreshing UI on login once login popup dismisses i.e. in e-commerce apps, etc. In my opinion, don't be too dependent on it. Use it as minimally as possible. Your opinion and approaches are welcome in the comments.
You can check my articles on Medium also.
Top comments (2)
Very informative đź’Ż
Thanks