EventBus egy Android könyvtár, amely a Publisher-Subscriber mintát valósítja meg egy eseménybuszon keresztül, lehetővé téve az adatcserét komponensek között közvetlen függőség nélkül. A GreenRobot által fejlesztett könyvtár leegyszerűsíti a kommunikációt az Activity, Fragment, Service és Background Thread között. A GitHub adatai szerint (2025) az EventBus több mint 25 ezer csillaggal rendelkezik, és több ezer Android alkalmazásban használják. A fő műveletek a subscribe (feliratkozás eseményre), a post (esemény küldése) és a sticky event (késleltetett esemény új feliratkozók számára).
Főbb pontok
EventBus egy eseménybusz könyvtár Android-hoz, amely a Publisher-Subscriber (kiadó-feliratkozó) mintát valósítja meg. Lehetővé teszi események továbbítását az alkalmazás komponensei (Activity, Fragment, Service, ViewModel) között anélkül, hogy explicit függőségeket hozna létre közöttük. Ellentétben a szabványos Android mechanizmusokkal (Intent, BroadcastReceiver), az EventBus a folyamaton belül működik, és nem használ IPC-t. A könyvtár teljesítményre van optimalizálva, és nem használ reflexiót, ha a Subscriber Index megfelelően van konfigurálva.
Az EventBus architektúrája három kulcselemből áll: Event (POJO osztály adatokkal), Subscriber (objektum @Subscribe jelölt metódusokkal) és EventBus (központi diszpécser). A feliratkozó az EventBus.getDefault().register(this) segítségével regisztrál, a leiratkozás az unregister(this) segítségével történik. Az események típusosak: a kezelők egy adott eseményosztályra iratkoznak fel, és csak az adott osztály vagy alosztályai eseményének post-olásakor hívódnak meg.
// POJO esemény
data class MessageEvent(
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
// Feliratkozó Activity-ben
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
}
}
// Esemény küldése másik komponensből
EventBus.getDefault().post(MessageEvent("Hello from Service"))
Alapértelmezés szerint az EventBus reflexiót használ a @Subscribe metódusok megtalálásához a register()-nél. Subscriber Index a fordítási fázisban generál egy indexet a kezelőkről egy annotáció-feldolgozó segítségével. Ez megszünteti a reflexió többletterhelését és felgyorsítja a regisztrációt. Az aktiváláshoz adja hozzá az eventbus-annotation-processor-t a build.gradle-hez. Az EventBus automatikusan használja az indexet, ha az elérhető a classpath-ban. Index nélkül a könyvtár továbbra is működik, de enyhe teljesítménycsökkenéssel.
Az EventBus.getDefault().post(event) meghívásakor a könyvtár meghatározza az esemény típusát, megkeresi az összes regisztrált feliratkozót @Subscribe metódusokkal, amelyek elfogadják ezt a típust, és meghívja őket a megadott ThreadMode szerint. A feliratkozók keresése a Class → CopyOnWriteArrayList
A feliratkozónak onStart()-ban kell regisztrálnia és onStop()-ban leiratkoznia. Ha onCreate()-ban regisztrál és onDestroy()-ban iratkozik le, az onDestroy meghívása nélkül megsemmisült Activity (a finish() miatt) a feliratkozók listáján maradhat. Feliratkozó szivárgás — az EventBus egyik fő problémája: a feliratkozók listáján maradt Activity-t a GC nem gyűjti be, amíg le nem iratkozik. Mindig párosítsa a register/unregister-t a megfelelő lifecycle metódusokban.
A @Subscribe annotáció támogatja a priority paramétert (egész szám, alapértelmezett 0). A magasabb prioritású kezelők korábban hívódnak meg. cancelEventDelivery() lehetővé teszi az esemény további feliratkozókhoz történő kézbesítésének megszakítását. Ez hasznos a prioritásos kezelőknél (naplózás, hitelesítés), amelyek megszakíthatják az esemény feldolgozását az alacsonyabb feliratkozók által. A funkció csak az esemény küldési szálában érhető el.
// Komplex példa prioritással
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)
}
}
// Esemény küldése
EventBus.getDefault().post(NavigationEvent("profile", bundle))
Az Android több mechanizmust kínál a folyamaton belüli kommunikációhoz: EventBus, LocalBroadcastManager (elavult) és LiveData/Flow. Mindegyiknek megvannak az előnyei és hátrányai. A választás az architekturális megközelítéstől és a teljesítménykövetelményektől függ. A Google modern ajánlásai a LiveData és Flow felé hajlanak a Lifecycle integráció és a szivárgások hiánya miatt.
| Jellemző | EventBus | LocalBroadcastManager | LiveData / Flow |
|---|---|---|---|
| Típusosság | Eseményosztályon keresztül | Intent filter (String) által | Generikus típus által |
| Lifecycle-aware | Nem (manuális leiratkozás) | Nem (manuális leiratkozás) | Igen (automatikus) |
| Sticky | Igen (postSticky) | Nem | Igen (LiveData — mindig sticky) |
| ThreadMode | MAIN, POSTING, BACKGROUND, ASYNC | Csak main | observe/observeOn által |
| Teljesítmény | Magas (Subscriber Index) | Közepes (IPC csomagolás) | Magas (megfigyelés) |
Az EventBus hasznos örökölt kóddal rendelkező projektekben (legacy code) és ahol a LiveData/Flow nem elérhető (Java-only projektek). Az EventBus Sticky events funkciója olyan rugalmasságot nyújt, ami hiányzik a LocalBroadcastManager-ből. Az EventBus egyszerűbb az események Service-ből Activity-be küldésére ViewModel nélkül — különösen, amikor egy háttérfeladat előrehaladásáról kell értesíteni. A könyvtár minimális méretű (körülbelül 50 KB) és nem ad hozzá függőségeket.
LiveData és Flow az Android Jetpack részét képezik, és integrálva vannak a Lifecycle-lel. Automatikusan leiratkoznak a komponens megsemmisülésekor, kiküszöbölve a memóriaszivárgásokat. A Flow támogatja a korutinokat és az összetett transzformációs operátorokat. A Google a LiveData-t ajánlja az UI réteghez, a Flow-t pedig a repository-khoz. Az EventBus a modulok közötti eseményekre marad, ahol a navigáció és az üzleti logika nem illeszkedik az MVVM-be.
Subscribe — eseménykezelő regisztrációja a @Subscribe annotáción keresztül. A metódusnak public, void típusúnak kell lennie, és pontosan egy paramétert kell fogadnia — az esemény típusát. Post — esemény küldése az összes feliratkozott kezelőnek az EventBus.getDefault().post(event) segítségével. A post metódus nem ad vissza eredményt, és nem tájékoztat arról, hogy hány kezelő lett meghívva. Választ igénylő eseményekhez használjon külön Event osztályt egy eredmény mezővel.
Az esemény bármely Java/Kotlin osztály lehet. Javasolt data class használata megváltoztathatatlan eseményekhez és sima osztály a mutable mezőkkel rendelkező eseményekhez. Az események elnevezésének tükröznie kell a műveletet: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Kerülje az egy közös Event osztályt String type mezővel — ez megfosztja a típusosság előnyeitől. Az események hierarchiája (szülő Event) lehetővé teszi a kapcsolódó események csoportjára való feliratkozást.
// Események hierarchiája
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()
// Feliratkozás alaposztályra
class SessionManager {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserEvent(event: UserEvent) {
when (event) {
is UserLoggedIn -> startSession(event.userId)
is UserLoggedOut -> endSession(event.reason)
}
}
}
// Küldés
EventBus.getDefault().post(UserLoggedIn("user_123"))
Az EventBus.getDefault().register(this) hívása beolvassa a feliratkozó osztályát reflexió vagy Subscriber Index segítségével, és eltárolja a talált @Subscribe metódusokat az eseménytérképben. Unregister eltávolítja a feliratkozót a térképből. Újraregisztráció leiratkozás nélkül — hiba (MultipleSubscriberException lép fel). Fragment esetén regisztráljon onStart()-ban és iratkozzon le onStop()-ban. Service esetén — onCreate()-ban és onDestroy()-ban. ViewModel esetén nem ajánlott — használjon LiveData-t.
Sticky event — egy esemény, amely elküldés után megőrződik az EventBus-ban. A postSticky() után regisztráló új feliratkozók azonnal megkapják a megfelelő típusú utolsó sticky-eseményt. Ez kényelmes a kezdeti állapot továbbításához: a képernyő megnyitásakor megkapja a regisztráció előtt elküldött utolsó adatokat. A sticky-esemény törlése az EventBus.getDefault().removeStickyEvent(Class) segítségével lehetséges.
A ThreadMode meghatározza, hogy a kezelő melyik szálban hívódik meg. POSTING (alapértelmezett) — a kezelő ugyanabban a szálban hajtódik végre, ahol a post-ot meghívták. MAIN — a kezelő a main szálban hajtódik végre Handler segítségével. BACKGROUND — a kezelő background szálban hajtódik végre; ha a post-ot main szálban hívták meg, az EventBus a kezelőt a background szál sorába helyezi. ASYNC — minden kezelő egy külön background szálban hajtódik végre a szálkészletből. UI frissítésekhez használja a MAIN-t.
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)
// Sticky-esemény küldése a LocationService-ből
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))
// A feliratkozó azonnal megkapja az utolsó helyadatot a regisztráció után
class MapFragment : Fragment() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
// Azonnal megkapja a LocationEvent-et, ha volt 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)
}
}
// Sticky-esemény törlése
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)
BACKGROUND egy background szálat használ az összes kezelő számára — egymás után hajtódnak végre. ASYNC új szálat hoz létre a készletből minden kezelő számára — párhuzamosan hajtódnak végre. A BACKGROUND alkalmas bemeneti-kimeneti műveletekhez megosztott adatbázissal. ASYNC — független hosszú műveletekhez (hálózati kérések). Mindkét mód thread-safe hozzáférést igényel a megosztott erőforrásokhoz. Szálak száma: az ASYNC készlet korlátlan.
Az EventBus használatakor a fejlesztők gyakran követnek el hibákat, amelyek memóriaszivárgáshoz, váratlan hívásokhoz és teljesítménycsökkenéshez vezetnek. A legkritikusabbak: elfelejtett leiratkozás Activity-ben, regisztráció onCreate-ban (nem onStart/onStop), feliratkozás Object-re (minden esemény), események küldése végtelen ciklusban. Az Android Profiler segítségével történő profilozás segít a problémák azonosításában.
A leggyakoribb hiba — Activity regisztrációja onCreate()-ban anélkül, hogy leiratkozna onDestroy()-ban. Eredmény: Az EventBus referenciát tart az Activity-re, a GC nem tudja felszabadítani. A képernyő elforgatásakor új Activity jön létre, az előző a memóriában marad. Megoldás: mindig párosítsa a register/unregister-t onStart/onStop-ban. Fragment esetén használja ugyanazt a sémát. Ha az Activity-t az EventBus a finish után is megtartja, ellenőrizze a Memory Profiler segítségével.
Subscriber Index nélkül az EventBus reflexiót használ a @Subscribe metódusok megtalálásához minden register()-nél. Android 6-7 eszközökön a reflexió lassan működik, akár 50 ms késleltetést okozva. A Subscriber Index teljesen megszünteti a reflexiót: a metódusok a fordítási fázisban indexelődnek egy annotáció-feldolgozó segítségével. 20+ feliratkozóval rendelkező projekteknél az index kötelező. Ellenőrizze, hogy a kapt vagy annotationProcessor csatlakoztatva van-e a build.gradle-ben.
// build.gradle (app) — Subscriber Index csatlakoztatása
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Kotlin esetén használjon kapt-ot
plugins {
id 'kotlin-kapt'
}
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Az index konfigurációja (defaultConfig-ban)
kapt {
arguments {
arg('eventBusIndex', 'com.app.EventBusIndex')
}
}
A Kotlin és Jetpack Compose alapú modern projektek a SharedFlow és Channel használatát részesítik előnyben a kotlinx.coroutines könyvtárból. A SharedFlow támogatja a replay (sticky), buffering és backpressure funkciókat. A Channel — egyszeri eseményeket (toast, navigáció). Mindkét megoldás integrálva van a Lifecycle-lel a repeatOnLifecycle segítségével, és nem igényel manuális leiratkozást. Új projektekhez a SharedFlow ajánlott az EventBus helyett. Meglévő projekteknél a migráció refaktoráláskor indokolt.
Gyakran ismételt kérdések
EventBus egy eseménybusz az adatcseréhez bármely komponens között (Activity, Fragment, Service). LiveData egy lifecycle-aware csomagoló az UI komponens által megfigyelt adatokhoz. A LiveData automatikusan kezeli a feliratkozást a Lifecycle-en keresztül. Az EventBus manuális register/unregister-t igényel. A LiveData az UI réteghez ajánlott, az EventBus — a modulok közötti kommunikációhoz, ahol a LiveData kényelmetlen.
Sticky event — egy esemény, amely elküldés után megőrződik az EventBus-ban. A postSticky() után regisztráló új feliratkozók azonnal megkapják az utolsó sticky-eseményt. A kezdeti állapothoz használják: a képernyő megnyitásakor megkapja az utolsó adatokat ismételt kérés nélkül. Törlése a removeStickyEvent() segítségével vagy új sticky-esemény elküldésekor történik.
Igen, az EventBus thread-safe. A post() hívása bármely szálból lehetséges. Az esemény feliratkozókhoz történő kézbesítése a könyvtáron belül szinkronizálódik. A ThreadMode meghatározza a kezelő végrehajtási szálát: MAIN (main szál Handler-en keresztül), POSTING (küldő szál), BACKGROUND (háttérfeladatok sora), ASYNC (külön szál). UI frissítésekhez használja a MAIN-t, nehéz műveletekhez — ASYNC-et.
Kapcsolja be a naplózást a EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install() segítségével. Iratkozzon fel a NoSubscriberEvent-re a kezelő nélküli események nyomon követéséhez. Használja a SubscriberExceptionEvent-t a globális kivételkezeléshez. Az Android Profiler segít a szivárgások megtalálásában. Összetett forgatókönyvekhez írjon tesztet: EventBus.getDefault().register(mock) + post(event) + verify(mock).
Nem, az EventBus (GreenRobot) az Android SDK-hoz és JVM-hez van kötve. Kotlin Multiplatform-hoz használja a Kotlin Multiplatform SharedFlow vagy KMMBus — a megosztott kódot támogató könyvtárakat. Az EventBus a KMM projekt Android oldalán működik, de nem érhető el a commonMain-ben. A cross-platform eseményekhez a platform natív mechanizmusai vagy az expect/actual segítségével történő absztrakció előnyösebb.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is