이벤트버스는 과거에 이벤트 기반의 아키텍처를 구현하기 위해 사용 되었었던 방식이다.
최근에 Java → Kotlin으로의 대체가 시작되면서 이 접근방식은 점점 KotlinFlow, Livedata와같은 subscribe / publish 아키텍처 구현방식으로 변화해 갔다.
Kotlin 1.4이후로 SharedFlows, StateFlow가 안정적인 버전이 되면서 EventBus 디자인 패턴에 접근하기 더욱 쉬어졌고 점차 다시 사용되기 시작했다.
특히 앱이 점점 더 복잡해 질수록 Fragment에서 다른 멀리 있는 Fragment에게 이벤트를 전달해야 할때 ViewModel, FragmentResultAPI로는 한계가 있다.
이벤트 버스 패턴은 세가지로 구성되어있다.
이벤트는 버스에 게시할 수 있으며 상태 변경을 나타낸다.
버스는 채널의 역할을 한다. 해당 버스에 이벤트가 게시되면 모든 Subscriber들에게 이벤트를 전달해준다.
버스와 상호작용하며 이벤트를 만들고 게시한다.
해당 패턴의 이점은 익명성에 비동기라는 점이다.
이벤트 버스 패턴은 게시자와 구독자가 누구인지 알 필요가 없고 오로지 이벤트가 변경되었는지만을 체크하면 됩니다.
메모리 수요의 증가 ( 구독자가 이벤트 데이터를 사용할 권한이 없더라도 모든 구독자들에게 복제해주어야한다.)
따라서 구독자에서 필터링을 해야한다.
컴포넌트 관계가 명확하지않다.(한가지 이벤트를 여러 컴포넌트에서 접근시 어디서 보냈는지가 불명확하다.)
기존 Observer 패턴과 다른점은 종속, 익명성이라고 할 수 있다.
기본적으로 Observer 패턴은 어플리케이션의 특정 범위에 종속된다.
따라서 기본적으로 생명주기를 따르게 된다.
하지만 EventBus패턴은 일반적으로 어플 전체 또는 모듈에 종속되어있다.
그리고 게시자와 구독자가 누구인지에 관심은 없고 오로지 어떤 이벤트가 게시되고 관찰당하는지에만 관심이 있다.
class EventBus {
private val _events = MutableSharedFlow<AppEvent>()
val events = _events.asSharedFlow()
suspend fun invokeEvent(event: AppEvent) = _events.emit(event)
}
enum class AppEvent {
LOGOUT;
}
class PublisherViewModel : ViewModel(), KoinComponent {
private val eventBus by inject<EventBus>()
fun logout() = eventBus.invokeEvent(AppEvent.LOGOUT)
}
class SubscriberController : KoinComponent {
private val eventBus by inject<EventBus>()
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
init {
scope.launch {
eventBus.events.filter { event -> event == EventBus.AppEvent.LOGOUT }.collectLatest { handleLogout() }
}
}
private fun handleLogout() {
// Here you'd handle the logout event
}
}