EventBus är ett bibliotek för Android som implementerar mönstret Publisher-Subscriber via en händelsebuss, vilket möjliggör datautbyte mellan komponenter utan direkt beroende. Utvecklat av GreenRobot, förenklar biblioteket kommunikation mellan Activity, Fragment, Service och Background Thread. Enligt GitHub-data (2025) har EventBus över 25 tusen stjärnor och används i tusentals Android-applikationer. Huvudoperationerna är subscribe (prenumerera på händelse), post (skicka händelse) och sticky event (fördröjd händelse för nya prenumeranter).
Huvudpunkter
EventBus är ett händelsebussbibliotek för Android som implementerar mönstret Publisher-Subscriber (utgivare-prenumerant). Det gör det möjligt att överföra händelser mellan applikationskomponenter (Activity, Fragment, Service, ViewModel) utan att skapa explicita beroenden mellan dem. Till skillnad från standard Android-mekanismer (Intent, BroadcastReceiver) fungerar EventBus inom processen och använder inte IPC. Biblioteket är optimerat för prestanda och använder inte reflektion när Subscriber Index är korrekt konfigurerat.
EventBus-arkitekturen består av tre nyckelelement: Event (POJO-klass med data), Subscriber (objekt med metoder markerade med @Subscribe) och EventBus (central dispatcher). Prenumeranten registrerar sig via EventBus.getDefault().register(this), avregistrering — via unregister(this). Händelser är typade: hanterare prenumererar på en specifik händelseklass och anropas endast vid post av en händelse av denna klass eller dess underklasser.
// POJO-händelse
data class MessageEvent(
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
// Prenumerant i 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
}
}
// Sändning av händelse från en annan komponent
EventBus.getDefault().post(MessageEvent("Hello from Service"))
Som standard använder EventBus reflektion för att hitta @Subscribe-metoder vid register(). Subscriber Index genererar ett index över hanterare under kompileringsfasen via en annoteringsprocessor. Detta eliminerar overhead från reflektion och snabbar upp registrering. För att aktivera, lägg till eventbus-annotation-processor i build.gradle. EventBus använder automatiskt indexet om det är tillgängligt i classpath. Utan index fungerar biblioteket fortfarande, men med något lägre prestanda.
Vid anrop av EventBus.getDefault().post(event) bestämmer biblioteket händelsetypen, hittar alla registrerade prenumeranter med @Subscribe-metoder som accepterar denna typ och anropar dem enligt angiven ThreadMode. Sökning efter prenumeranter utförs via kartan Class → CopyOnWriteArrayList
Prenumeranten bör registrera sig i onStart() och avregistrera sig i onStop(). Om den registrerar sig i onCreate() och avregistrerar sig i onDestroy(), kan en Activity som förstörts utan anrop av onDestroy (på grund av finish()) finnas kvar i prenumerantlistan. Prenumerantläcka — ett av huvudproblemen med EventBus: en Activity som finns kvar i prenumerantlistan kommer inte att samlas in av GC förrän den avregistrerar sig. Para alltid ihop register/unregister i rätt lifecycle-metoder.
@Subscribe-annoteringen stöder parametern priority (heltal, standard 0). Hanterare med högre prioritet anropas tidigare. cancelEventDelivery() gör det möjligt att avbryta leveransen av händelsen till återstående prenumeranter. Detta är användbart för prioriterade hanterare (loggning, autentisering) som kan avbryta bearbetningen av händelsen av lägre prenumeranter. Funktionen är endast tillgänglig i tråden som skickar händelsen.
// Komplext exempel med prioritet
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)
}
}
// Sändning av händelse
EventBus.getDefault().post(NavigationEvent("profile", bundle))
Android erbjuder flera mekanismer för intraprocesskommunikation: EventBus, LocalBroadcastManager (föråldrad) och LiveData/Flow. Var och en har sina fördelar och nackdelar. Valet beror på arkitektonisk strategi och prestandakrav. Googles moderna rekommendationer lutar mot LiveData och Flow på grund av integration med Lifecycle och frånvaro av läckor.
| Egenskap | EventBus | LocalBroadcastManager | LiveData / Flow |
|---|---|---|---|
| Typning | Via händelseklass | Via Intent filter (String) | Via generisk typ |
| Lifecycle-aware | Nej (manuell avregistrering) | Nej (manuell avregistrering) | Ja (automatiskt) |
| Sticky | Ja (postSticky) | Nej | Ja (LiveData — alltid sticky) |
| ThreadMode | MAIN, POSTING, BACKGROUND, ASYNC | Endast main | Via observe/observeOn |
| Prestanda | Hög (Subscriber Index) | Medel (IPC-omslag) | Hög (observation) |
EventBus är användbart i projekt med äldre kod (legacy code) och där LiveData/Flow inte är tillgängliga (Java-only-projekt). EventBus Sticky events ger flexibilitet som saknas i LocalBroadcastManager. EventBus är också enklare för att skicka händelser från Service till Activity utan ViewModel — särskilt när man behöver meddela om framsteg i en bakgrundsuppgift. Biblioteket har en minimal storlek (cirka 50 KB) och lägger inga beroenden.
LiveData och Flow är en del av Android Jetpack och är integrerade med Lifecycle. De avregistrerar sig automatiskt vid komponentförstöring, vilket eliminerar minnesläckor. Flow stöder korutiner och komplexa transformationsoperatorer. Google rekommenderar LiveData för UI-lagret och Flow för datalager. EventBus finns kvar för tvärmodulhändelser där navigering och affärslogik inte passar in i MVVM.
Subscribe — registrering av en händelsehanterare via @Subscribe-annoterigen. Metoden måste vara public, void och acceptera exakt en parameter — händelsetypen. Post — sändning av händelsen till alla prenumererade hanterare via EventBus.getDefault().post(event). Metoden post returnerar inget resultat och meddelar inte hur många hanterare som anropats. För händelser med svar, använd en separat Event-klass med ett fält för resultatet.
En händelse — vilken Java/Kotlin-klass som helst. Det rekommenderas att använda data class för oföränderliga händelser och vanlig klass för händelser med mutable-fält. Namngivning av händelser bör återspegla åtgärden: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Undvik en gemensam Event-klass med ett String type-fält — detta tar bort fördelarna med typning. Händelsehierarki (förälder-Event) möjliggör prenumeration på en grupp relaterade händelser.
// Händelsehierarki
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()
// Prenumeration på basklass
class SessionManager {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserEvent(event: UserEvent) {
when (event) {
is UserLoggedIn -> startSession(event.userId)
is UserLoggedOut -> endSession(event.reason)
}
}
}
// Sändning
EventBus.getDefault().post(UserLoggedIn("user_123"))
Anropet EventBus.getDefault().register(this) skannar prenumerantens klass via reflektion eller Subscriber Index och lagrar de hittade @Subscribe-metoderna i händelsekartan. Unregister tar bort prenumeranten från kartan. Återregistrering utan avregistrering — fel (MultipleSubscriberException uppstår). För Fragment, registrera i onStart() och avregistrera i onStop(). För Service — i onCreate() och onDestroy(). För ViewModel rekommenderas inte — använd LiveData.
Sticky event — en händelse som bevaras i EventBus efter sändning. Nya prenumeranter som registrerar sig efter postSticky() får omedelbart den senaste sticky-händelsen av motsvarande typ. Detta är bekvämt för överföring av initialt tillstånd: vid öppning av en skärm får den de senaste data som skickats före dess registrering. Borttagning av sticky-händelse kan göras via EventBus.getDefault().removeStickyEvent(Class).
ThreadMode bestämmer i vilken tråd hanteraren anropas. POSTING (standard) — hanteraren exekveras i samma tråd där post anropades. MAIN — hanteraren exekveras i main-tråden via Handler. BACKGROUND — hanteraren exekveras i en bakgrundstråd; om post anropades i main-tråden placerar EventBus hanteraren i bakgrundstrådens kö. ASYNC — varje hanterare exekveras i en separat bakgrundstråd från trådpoolen. För UI-uppdateringar, använd MAIN.
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)
// Sändning av sticky-händelse från LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))
// Prenumeranten får den senaste platsen omedelbart efter registrering
class MapFragment : Fragment() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
// Får omedelbart LocationEvent om postSticky fanns
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onLocationEvent(event: LocationEvent) {
moveMapTo(event.lat, event.lng)
}
}
// Borttagning av sticky-händelse
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)
BACKGROUND använder en bakgrundstråd för alla hanterare — de exekveras sekventiellt. ASYNC skapar en ny tråd från poolen för varje hanterare — de exekveras parallellt. BACKGROUND är lämpligt för in-/utdataoperationer med en delad databas. ASYNC — för oberoende långa operationer (nätverksförfrågningar). Båda lägena kräver trådsäker åtkomst till delade resurser. Antal trådar: ASYNC-poolen är obegränsad.
Vid användning av EventBus gör utvecklare ofta misstag som leder till minnesläckor, oväntade anrop och prestandaförsämring. De mest kritiska: glömd avregistrering i Activity, registrering i onCreate (istället för onStart/onStop), prenumeration på Object (alla händelser), sändning av händelser i en oändlig loop. Profilering via Android Profiler hjälper till att identifiera problem.
Det vanligaste felet — registrering av Activity i onCreate() utan avregistrering i onDestroy(). Resultat: EventBus behåller en referens till Activity, GC kan inte frigöra den. Vid skärmrotation skapas en ny Activity, den föregående finns kvar i minnet. Lösning: para alltid ihop register/unregister i onStart/onStop. För Fragment, använd samma schema. Om Activity hålls kvar av EventBus efter finish, kontrollera via Memory Profiler.
Utan Subscriber Index använder EventBus reflektion för att hitta @Subscribe-metoder vid varje register(). På enheter med Android 6-7 fungerar reflektion långsamt, vilket orsakar fördröjningar på upp till 50 ms. Subscriber Index eliminerar reflektion helt: metoder indexeras under kompileringsfasen via en annoteringsprocessor. För projekt med 20+ prenumeranter är indexet obligatoriskt. Kontrollera att kapt eller annotationProcessor är ansluten i build.gradle.
// build.gradle (app) — anslutning av Subscriber Index
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Använd kapt för Kotlin
plugins {
id 'kotlin-kapt'
}
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Konfiguration av index (i defaultConfig)
kapt {
arguments {
arg('eventBusIndex', 'com.app.EventBusIndex')
}
}
Moderna projekt på Kotlin och Jetpack Compose föredrar SharedFlow och Channel från biblioteket kotlinx.coroutines. SharedFlow stöder replay (sticky), buffering och backpressure. Channel — engångshändelser (toast, navigering). Båda lösningarna är integrerade med Lifecycle via repeatOnLifecycle och kräver ingen manuell avregistrering. För nya projekt rekommenderas SharedFlow istället för EventBus. För befintliga projekt är migrering motiverad vid omfaktorering.
Vanliga frågor
EventBus är en händelsebuss för datautbyte mellan valfria komponenter (Activity, Fragment, Service). LiveData är ett lifecycle-aware omslag för data som observeras av en UI-komponent. LiveData hanterar automatiskt prenumeration via Lifecycle. EventBus kräver manuellt register/unregister. LiveData rekommenderas för UI-lagret, EventBus — för tvärmodulskommunikation där LiveData är obekvämt.
Sticky event — en händelse som bevaras i EventBus efter sändning. Nya prenumeranter som registrerar sig efter postSticky() får omedelbart den senaste sticky-händelsen. Används för initialt tillstånd: vid öppning av en skärm får den senaste data utan upprepad begäran. Tas bort via removeStickyEvent() eller vid sändning av en ny sticky-händelse av samma typ.
Ja, EventBus är trådsäker. Anrop av post() är möjligt från vilken tråd som helst. Leverans av händelse till prenumeranter synkroniseras inom biblioteket. ThreadMode bestämmer hanterarens exekveringstråd: MAIN (main-tråd via Handler), POSTING (avsändartråd), BACKGROUND (kö av bakgrundsuppgifter), ASYNC (separat tråd). För UI-uppdateringar, använd MAIN, för tunga operationer — ASYNC.
Aktivera loggning via EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Prenumerera på NoSubscriberEvent för spårning av händelser utan hanterare. Använd SubscriberExceptionEvent för global undantagshantering. Android Profiler hjälper att hitta läckor. För komplexa scenarier, skriv ett test: EventBus.getDefault().register(mock) + post(event) + verify(mock).
Nej, EventBus (GreenRobot) är bundet till Android SDK och JVM. För Kotlin Multiplatform, använd Kotlin Multiplatform SharedFlow eller KMMBus — bibliotek som stöder delad kod. EventBus fungerar på Android-sidan av ett KMM-projekt men är inte tillgängligt i commonMain. För plattformsoberoende händelser är nativa plattformsmekanismer eller abstraktion via expect/actual att föredra.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också