EventBus je knihovna pro Android implementující vzor Publisher-Subscriber pomocí událostní sběrnice, umožňující výměnu dat mezi komponentami bez přímé závislosti. Vyvinutá společností GreenRobot, knihovna zjednodušuje komunikaci mezi Activity, Fragment, Service a Background Thread. Podle údajů GitHub (2025) má EventBus více než 25 tisíc hvězdiček a používá se v tisících Android aplikací. Hlavní operace jsou subscribe (přihlášení k odběru události), post (odeslání události) a sticky event (zpožděná událost pro nové odběratele).
Hlavní body
EventBus je knihovna událostní sběrnice pro Android implementující vzor Publisher-Subscriber (vydavatel-odběratel). Umožňuje přenášet události mezi komponentami aplikace (Activity, Fragment, Service, ViewModel) bez vytváření explicitních závislostí mezi nimi. Na rozdíl od standardních Android mechanismů (Intent, BroadcastReceiver) pracuje EventBus uvnitř procesu a nepoužívá IPC. Knihovna je optimalizována pro výkon a nepoužívá reflexi při správně nakonfigurovaném Subscriber Index.
Architektura EventBus se skládá ze tří klíčových prvků: Event (POJO třída s daty), Subscriber (objekt s metodami označenými @Subscribe) a EventBus (centrální dispečer). Odběratel se registruje pomocí EventBus.getDefault().register(this), odhlášení — pomocí unregister(this). Události jsou typované: obsluhy se přihlašují k odběru konkrétní třídy události a jsou volány pouze při post události této třídy nebo jejích podtříd.
// Událost POJO
data class MessageEvent(
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
// Odběratel v Activity
class MainActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: MessageEvent) {
textView.text = event.message
}
}
// Odeslání události z jiné komponenty
EventBus.getDefault().post(MessageEvent("Hello from Service"))
Ve výchozím nastavení EventBus používá reflexi k nalezení @Subscribe metod při register(). Subscriber Index generuje index obsluh ve fázi kompilace pomocí procesoru anotací. Tím se eliminuje režie reflexe a urychluje se registrace. Pro aktivaci přidejte eventbus-annotation-processor do build.gradle. EventBus automaticky používá index, pokud je k dispozici v classpath. Bez indexu knihovna stále funguje, ale s mírným poklesem výkonu.
Při volání EventBus.getDefault().post(event) knihovna určí typ události, najde všechny registrované odběratele s metodami @Subscribe přijímajícími tento typ a zavolá je podle zadaného ThreadMode. Vyhledávání odběratelů probíhá podle mapy Class → CopyOnWriteArrayList
Odběratel by se měl zaregistrovat v onStart() a odhlásit v onStop(). Pokud se zaregistruje v onCreate() a odhlásí v onDestroy(), Activity zničená bez volání onDestroy (kvůli finish()) může zůstat v seznamu odběratelů. Únik odběratele — jeden z hlavních problémů EventBus: Activity zbývající v seznamu odběratelů nebude shromážděna GC, dokud se neodhlásí. Vždy párujte register/unregister ve správných lifecycle metodách.
Anotace @Subscribe podporuje parametr priority (celé číslo, výchozí 0). Obsluhy s vyšší prioritou jsou volány dříve. cancelEventDelivery() umožňuje přerušit doručení události zbývajícím odběratelům. To je užitečné pro prioritní obsluhy (logování, autentizace), které mohou zrušit zpracování události nižšími odběrateli. Funkce je dostupná pouze ve vlákně odesílání události.
// Složitý příklad s prioritou
data class NavigationEvent(val screen: String, val data: Bundle)
class NavigationInterceptor {
@Subscribe(priority = 10, threadMode = ThreadMode.POSTING)
fun onNavigationEvent(event: NavigationEvent) {
if (event.screen == "restricted" && !isAuthorized) {
EventBus.getDefault().cancelEventDelivery(event)
}
}
}
class AnalyticsLogger {
@Subscribe(priority = 5)
fun logNavigation(event: NavigationEvent) {
analytics.logScreen(event.screen)
}
}
// Odeslání události
EventBus.getDefault().post(NavigationEvent("profile", bundle))
Android nabízí několik mechanismů pro vnitroprocesovou komunikaci: EventBus, LocalBroadcastManager (zastaralý) a LiveData/Flow. Každý má své výhody a nevýhody. Volba závisí na architektonickém přístupu a požadavcích na výkon. Moderní doporučení Google se přiklánějí k LiveData a Flow kvůli integraci s Lifecycle a absenci úniků.
| Vlastnost | EventBus | LocalBroadcastManager | LiveData / Flow |
|---|---|---|---|
| Typizace | Pomocí třídy události | Pomocí Intent filter (String) | Pomocí generického typu |
| Lifecycle-aware | Ne (ruční odhlášení) | Ne (ruční odhlášení) | Ano (automaticky) |
| Sticky | Ano (postSticky) | Ne | Ano (LiveData — vždy sticky) |
| ThreadMode | MAIN, POSTING, BACKGROUND, ASYNC | Pouze main | Pomocí observe/observeOn |
| Výkon | Vysoký (Subscriber Index) | Střední (IPC obal) | Vysoký (pozorování) |
EventBus je užitečný v projektech s dědictvím (legacy code) a tam, kde LiveData/Flow nejsou k dispozici (projekty Java-only). Sticky events EventBus poskytují flexibilitu, která chybí v LocalBroadcastManager. EventBus je také jednodušší pro odesílání událostí z Service do Activity bez ViewModel — zvláště když je třeba informovat o průběhu úlohy na pozadí. Knihovna má minimální velikost (asi 50 KB) a nepřidává závislosti.
LiveData a Flow jsou součástí Android Jetpack a jsou integrovány s Lifecycle. Automaticky se odhlašují při zničení komponenty, čímž eliminují úniky paměti. Flow podporuje korutiny a složité transformační operátory. Google doporučuje LiveData pro vrstvu UI a Flow pro repozitáře. EventBus zůstává pro modulové události, kde navigace a obchodní logika nezapadají do MVVM.
Subscribe — registrace obsluhy události pomocí anotace @Subscribe. Metoda musí být public, void a přijímat přesně jeden parametr — typ události. Post — odeslání události všem přihlášeným obsluhám pomocí EventBus.getDefault().post(event). Metoda post nevrací výsledek a neinformuje, kolik obsluh bylo voláno. Pro události s odpovědí použijte samostatnou třídu Event s polem pro výsledek.
Událost — libovolná třída Java/Kotlin. Doporučuje se používat data class pro neměnné události a běžnou třídu pro události s proměnnými poli. Pojmenování událostí by mělo odrážet akci: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Vyhněte se jedné společné třídě Event s polem String type — to zbavuje výhod typizace. Hierarchie událostí (rodičovský Event) umožňuje přihlásit se ke skupině příbuzných událostí.
// Hierarchie událostí
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()
// Přihlášení k odběru základní třídy
class SessionManager {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserEvent(event: UserEvent) {
when (event) {
is UserLoggedIn -> startSession(event.userId)
is UserLoggedOut -> endSession(event.reason)
}
}
}
// Odeslání
EventBus.getDefault().post(UserLoggedIn("user_123"))
Volání EventBus.getDefault().register(this) skenuje třídu odběratele pomocí reflexe nebo Subscriber Index a ukládá nalezené metody @Subscribe do mapy událostí. Unregister odstraňuje odběratele z mapy. Opětovná registrace bez odhlášení — chyba (dojde k MultipleSubscriberException). Pro Fragment se registrujte v onStart() a odhlašujte v onStop(). Pro Service — v onCreate() a onDestroy(). Pro ViewModel se nedoporučuje — použijte LiveData.
Sticky event — událost, která je po odeslání uchována v EventBus. Noví odběratelé registrovaní po postSticky() okamžitě obdrží poslední sticky-událost odpovídajícího typu. To je vhodné pro přenos počátečního stavu: při otevření obrazovky obdrží poslední data odeslaná před jeho registrací. Smazání sticky-události lze provést pomocí EventBus.getDefault().removeStickyEvent(Class).
ThreadMode určuje, ve kterém vlákně je obsluha volána. POSTING (výchozí) — obsluha se provádí ve stejném vlákně, kde bylo voláno post. MAIN — obsluha se provádí v main vlákně pomocí Handleru. BACKGROUND — obsluha se provádí v background vlákně; pokud bylo post voláno v main vlákně, EventBus umístí obsluhu do fronty background vlákna. ASYNC — každá obsluha se provádí v samostatném background vlákně z fondu vláken. Pro aktualizace UI použijte MAIN.
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)
// Odeslání sticky-události z LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))
// Odběratel obdrží poslední polohu ihned po registraci
class MapFragment : Fragment() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
// Ihned obdrží LocationEvent, pokud bylo postSticky
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onLocationEvent(event: LocationEvent) {
moveMapTo(event.lat, event.lng)
}
}
// Smazání sticky-události
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)
BACKGROUND používá jedno background vlákno pro všechny obsluhy — provádějí se sekvenčně. ASYNC vytváří nové vlákno z fondu pro každou obsluhu — provádějí se paralelně. BACKGROUND je vhodný pro vstupně-výstupní operace se sdílenou databází. ASYNC — pro nezávislé dlouhé operace (síťové požadavky). Oba režimy vyžadují thread-safe přístup ke sdíleným zdrojům. Počet vláken: fond ASYNC je neomezený.
Při používání EventBus vývojáři často dělají chyby vedoucí k únikům paměti, neočekávaným voláním a poklesu výkonu. Nejkritičtější: zapomenuté odhlášení v Activity, registrace v onCreate (místo onStart/onStop), přihlášení k odběru Object (všechny události), odesílání událostí v nekonečné smyčce. Profilování pomocí Android Profiler pomáhá identifikovat problémy.
Nejčastější chyba — registrace Activity v onCreate() bez odhlášení v onDestroy(). Výsledek: EventBus uchovává referenci na Activity, GC ji nemůže uvolnit. Při otočení obrazovky se vytvoří nová Activity, předchozí zůstává v paměti. Řešení: vždy párujte register/unregister v onStart/onStop. Pro Fragment použijte stejné schéma. Pokud je Activity držena EventBus po finish, zkontrolujte pomocí Memory Profiler.
Bez Subscriber Index EventBus používá reflexi k nalezení @Subscribe metod při každém register(). Na zařízeních s Android 6-7 reflexe pracuje pomalu, způsobuje zpoždění až 50 ms. Subscriber Index zcela eliminuje reflexi: metody jsou indexovány ve fázi kompilace pomocí procesoru anotací. Pro projekty s 20+ odběrateli je index povinný. Zkontrolujte, zda je kapt nebo annotationProcessor připojen v build.gradle.
// build.gradle (app) — připojení Subscriber Index
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Pro Kotlin použijte kapt
plugins {
id 'kotlin-kapt'
}
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Konfigurace indexu (v defaultConfig)
kapt {
arguments {
arg('eventBusIndex', 'com.app.EventBusIndex')
}
}
Moderní projekty na Kotlin a Jetpack Compose preferují SharedFlow a Channel z knihovny kotlinx.coroutines. SharedFlow podporuje replay (sticky), buffering a backpressure. Channel — jednorázové události (toast, navigace). Obě řešení jsou integrována s Lifecycle pomocí repeatOnLifecycle a nevyžadují ruční odhlášení. Pro nové projekty se doporučuje SharedFlow místo EventBus. Pro existující projekty je migrace při refaktorování opodstatněná.
Často kladené otázky
EventBus je událostní sběrnice pro výměnu dat mezi libovolnými komponentami (Activity, Fragment, Service). LiveData je lifecycle-aware obal pro data pozorovaná UI komponentou. LiveData automaticky spravuje odběr prostřednictvím Lifecycle. EventBus vyžaduje ruční register/unregister. LiveData je doporučeno pro vrstvu UI, EventBus — pro modulovou komunikaci, kde je LiveData nepohodlný.
Sticky event — událost, která je po odeslání uchována v EventBus. Noví odběratelé registrovaní po postSticky() okamžitě obdrží poslední sticky-událost. Používá se pro počáteční stav: při otevření obrazovky obdrží poslední data bez opakovaného požadavku. Maže se pomocí removeStickyEvent() nebo při odeslání nové sticky-události stejného typu.
Ano, EventBus je thread-safe. Volání post() je možné z libovolného vlákna. Doručení události odběratelům je synchronizováno uvnitř knihovny. ThreadMode určuje vlákno provádění obsluhy: MAIN (main vlákno přes Handler), POSTING (vlákno odesílatele), BACKGROUND (fronta úloh na pozadí), ASYNC (samostatné vlákno). Pro aktualizace UI použijte MAIN, pro těžké operace — ASYNC.
Zapněte logování pomocí EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Přihlaste se k odběru NoSubscriberEvent pro sledování událostí bez obsluh. Použijte SubscriberExceptionEvent pro globální zpracování výjimek. Android Profiler pomáhá najít úniky. Pro složité scénáře napište test: EventBus.getDefault().register(mock) + post(event) + verify(mock).
Ne, EventBus (GreenRobot) je vázán na Android SDK a JVM. Pro Kotlin Multiplatform použijte Kotlin Multiplatform SharedFlow nebo KMMBus — knihovny podporující sdílený kód. EventBus na Android straně KMM projektu funguje, ale není dostupný v commonMain. Pro cross-platform události jsou preferovány nativní mechanismy platformy nebo abstrakce pomocí expect/actual.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také