EventBus es una biblioteca para Android que implementa el patrón Publisher-Subscriber a través de un bus de eventos, permitiendo intercambiar datos entre componentes sin dependencias directas. Desarrollada por GreenRobot, la biblioteca simplifica la comunicación entre Activity, Fragment, Service y Background Thread. Según datos de GitHub (2025), EventBus cuenta con más de 25 mil estrellas y se utiliza en miles de aplicaciones Android. Las operaciones principales son subscribe (suscripción a un evento), post (envío de un evento) y sticky event (evento diferido para nuevos suscriptores).
Puntos clave
EventBus es una biblioteca de bus de eventos para Android que implementa el patrón Publisher-Subscriber. Permite pasar eventos entre componentes de la aplicación (Activity, Fragment, Service, ViewModel) sin crear dependencias explícitas entre ellos. A diferencia de los mecanismos estándar de Android (Intent, BroadcastReceiver), EventBus funciona dentro del proceso y no utiliza IPC. La biblioteca está optimizada para el rendimiento y no usa reflexión cuando el Subscriber Index está configurado correctamente.
La arquitectura de EventBus consta de tres elementos clave: Event (clase POJO con datos), Subscriber (objeto con métodos anotados con @Subscribe) y EventBus (despachador central). El suscriptor se registra mediante EventBus.getDefault().register(this) y se da de baja mediante unregister(this). Los eventos están tipados: los manejadores se suscriben a una clase de evento específica y solo se invocan cuando se publica un evento de esa clase o sus subclases.
// Evento POJO
data class MessageEvent(
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
// Suscriptor en 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
}
}
// Envío de evento desde otro componente
EventBus.getDefault().post(MessageEvent("Hello from Service"))
Por defecto, EventBus usa reflexión para encontrar métodos @Subscribe durante register(). Subscriber Index genera un índice de manejadores en tiempo de compilación mediante un procesador de anotaciones. Esto elimina la sobrecarga de la reflexión y acelera el registro. Para habilitarlo, agregue eventbus-annotation-processor a build.gradle. EventBus usa el índice automáticamente si está disponible en el classpath. Sin el índice, la biblioteca sigue funcionando pero con una ligera disminución del rendimiento.
Cuando se llama a EventBus.getDefault().post(event), la biblioteca determina el tipo de evento, encuentra todos los suscriptores registrados con métodos @Subscribe que acepten ese tipo y los invoca según el ThreadMode especificado. La búsqueda de suscriptores se realiza mediante un mapa Class → CopyOnWriteArrayList
Un suscriptor debe registrarse en onStart() y darse de baja en onStop(). Si se registra en onCreate() y se da de baja en onDestroy(), una Activity destruida sin llamar a onDestroy (debido a finish()) puede permanecer en la lista de suscriptores. La fuga de suscriptores es uno de los principales problemas de EventBus: una Activity que permanece en la lista de suscriptores no será recolectada por el GC hasta que se dé de baja. Siempre empareje register/unregister en los métodos de ciclo de vida correctos.
La anotación @Subscribe admite un parámetro priority (entero, por defecto 0). Los manejadores con mayor prioridad se invocan primero. cancelEventDelivery() permite interrumpir la entrega del evento a los suscriptores restantes. Esto es útil para manejadores prioritarios (logging, autenticación) que pueden cancelar el procesamiento del evento por suscriptores downstream. Esta función solo está disponible en el hilo de envío del evento.
// Ejemplo complejo con prioridad
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)
}
}
// Envío de evento
EventBus.getDefault().post(NavigationEvent("profile", bundle))
Android ofrece varios mecanismos para la comunicación dentro del proceso: EventBus, LocalBroadcastManager (obsoleto) y LiveData/Flow. Cada uno tiene sus ventajas y desventajas. La elección depende del enfoque arquitectónico y los requisitos de rendimiento. Las recomendaciones modernas de Google se inclinan hacia LiveData y Flow debido a la integración con Lifecycle y la ausencia de fugas.
| Característica | EventBus | LocalBroadcastManager | LiveData / Flow |
|---|---|---|---|
| Tipado | Mediante clase de evento | Mediante filtro Intent (String) | Mediante tipo genérico |
| Lifecycle-aware | No (baja manual) | No (baja manual) | Sí (automático) |
| Sticky | Sí (postSticky) | No | Sí (LiveData siempre es sticky) |
| ThreadMode | MAIN, POSTING, BACKGROUND, ASYNC | Solo main | Mediante observe/observeOn |
| Rendimiento | Alto (Subscriber Index) | Medio (envoltorio IPC) | Alto (observación) |
EventBus es útil en proyectos con código heredado significativo y donde LiveData/Flow no están disponibles (proyectos solo Java). Los sticky events de EventBus brindan una flexibilidad ausente en LocalBroadcastManager. EventBus también es más fácil para enviar eventos desde Service a Activity sin ViewModel, especialmente cuando se necesita notificar el progreso de una tarea en segundo plano. La biblioteca tiene un tamaño mínimo (alrededor de 50 KB) y no agrega dependencias.
LiveData y Flow son parte de Android Jetpack y están integrados con Lifecycle. Se dan de baja automáticamente cuando un componente se destruye, eliminando las fugas de memoria. Flow admite corrutinas y operadores de transformación complejos. Google recomienda LiveData para la capa de UI y Flow para repositorios. EventBus sigue siendo útil para eventos entre módulos donde la navegación y la lógica de negocio no encajan en MVVM.
Subscribe es el registro de un manejador de eventos mediante la anotación @Subscribe. El método debe ser public, void y aceptar exactamente un parámetro — el tipo de evento. Post es el envío de un evento a todos los manejadores suscritos mediante EventBus.getDefault().post(event). El método post no devuelve un resultado ni indica cuántos manejadores fueron invocados. Para eventos con respuesta, use una clase Event separada con un campo de resultado.
Un evento es cualquier clase de Java/Kotlin. Se recomienda usar data class para eventos inmutables y una clase normal para eventos con campos mutables. El nombre del evento debe reflejar la acción: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Evite una clase Event genérica única con un campo String type — esto elimina los beneficios del tipado. Una jerarquía de eventos (Event padre) permite suscribirse a un grupo de eventos relacionados.
// Jerarquía de eventos
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()
// Suscripción a la clase base
class SessionManager {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserEvent(event: UserEvent) {
when (event) {
is UserLoggedIn -> startSession(event.userId)
is UserLoggedOut -> endSession(event.reason)
}
}
}
// Envío
EventBus.getDefault().post(UserLoggedIn("user_123"))
Llamar a EventBus.getDefault().register(this) escanea la clase del suscriptor mediante reflexión o Subscriber Index y almacena los métodos @Subscribe encontrados en el mapa de eventos. Unregister elimina al suscriptor del mapa. Volver a registrarse sin cancelar el registro es un error (lanzará MultipleSubscriberException). Para Fragment, regístrese en onStart() y cancele en onStop(). Para Service, en onCreate() y onDestroy(). Para ViewModel, no se recomienda — use LiveData.
Un sticky event es un evento que persiste en EventBus después de ser enviado. Los nuevos suscriptores registrados después de postSticky() reciben inmediatamente el último sticky event del tipo correspondiente. Esto es conveniente para pasar el estado inicial: al abrir una pantalla, recibe los últimos datos enviados antes de su registro. Puede eliminar un sticky event mediante EventBus.getDefault().removeStickyEvent(Class).
ThreadMode determina en qué hilo se ejecuta el manejador. POSTING (predeterminado) — el manejador se ejecuta en el mismo hilo donde se llamó a post. MAIN — el manejador se ejecuta en el hilo principal mediante Handler. BACKGROUND — el manejador se ejecuta en un hilo secundario; si post se llamó en el hilo principal, EventBus pone en cola el manejador en un hilo secundario. ASYNC — cada manejador se ejecuta en un hilo secundario separado de un grupo de hilos. Para actualizaciones de UI, use MAIN.
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)
// Envío de sticky event desde LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))
// El suscriptor recibe la última ubicación inmediatamente después del registro
class MapFragment : Fragment() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
// Recibirá inmediatamente LocationEvent si se llamó a 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)
}
}
// Eliminación de sticky event
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)
BACKGROUND usa un solo hilo en segundo plano para todos los manejadores — se ejecutan secuencialmente. ASYNC crea un nuevo hilo del grupo para cada manejador — se ejecutan en paralelo. BACKGROUND es adecuado para operaciones de E/S con una base de datos compartida. ASYNC es para operaciones largas independientes (solicitudes de red). Ambos modos requieren acceso thread-safe a recursos compartidos. Tenga en cuenta el número de hilos: el grupo ASYNC es ilimitado.
Al usar EventBus, los desarrolladores suelen cometer errores que provocan fugas de memoria, invocaciones inesperadas y degradación del rendimiento. Los más críticos: olvidar la cancelación del registro en Activity, registrarse en onCreate (en lugar de onStart/onStop), suscribirse a Object (todos los eventos), enviar eventos en un bucle infinito. La creación de perfiles con Android Profiler ayuda a identificar problemas.
El error más común es registrar una Activity en onCreate() sin cancelar el registro en onDestroy(). Resultado: EventBus mantiene una referencia a la Activity, el GC no puede liberarla. Al rotar la pantalla, se crea una nueva Activity mientras la anterior permanece en memoria. Solución: siempre empareje register/unregister en onStart/onStop. Para Fragment, use el mismo patrón. Si una Activity es retenida por EventBus después de finish, verifique con Memory Profiler.
Sin Subscriber Index, EventBus usa reflexión para encontrar métodos @Subscribe en cada register(). En dispositivos con Android 6-7, la reflexión es lenta, causando demoras de hasta 50 ms. Subscriber Index elimina la reflexión por completo: los métodos se indexan en tiempo de compilación mediante un procesador de anotaciones. Para proyectos con 20 o más suscriptores, el índice es obligatorio. Asegúrese de que kapt o annotationProcessor esté configurado en build.gradle.
// build.gradle (app) — agregar Subscriber Index
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Para Kotlin use kapt
plugins {
id 'kotlin-kapt'
}
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Configuración del índice (en defaultConfig)
kapt {
arguments {
arg('eventBusIndex', 'com.app.EventBusIndex')
}
}
Los proyectos modernos que usan Kotlin y Jetpack Compose prefieren SharedFlow y Channel de la biblioteca kotlinx.coroutines. SharedFlow admite replay (sticky), buffering y backpressure. Channel maneja eventos de una sola vez (toast, navegación). Ambas soluciones están integradas con Lifecycle mediante repeatOnLifecycle y no requieren cancelación manual de suscripción. Para proyectos nuevos, se recomienda SharedFlow en lugar de EventBus. Para proyectos existentes, la migración está justificada durante la refactorización.
Preguntas frecuentes
EventBus es un bus de eventos para el intercambio de datos entre cualquier componente (Activity, Fragment, Service). LiveData es un envoltorio lifecycle-aware para datos observados por un componente de UI. LiveData gestiona automáticamente la suscripción a través de Lifecycle. EventBus requiere register/unregister manual. LiveData se recomienda para la capa de UI, EventBus para la comunicación entre módulos donde LiveData es incómodo.
Un sticky event es un evento que persiste en EventBus después de ser enviado. Los nuevos suscriptores registrados después de postSticky() reciben inmediatamente el último sticky event. Se usa para el estado inicial: al abrir una pantalla, recibe los últimos datos sin una nueva solicitud. Se elimina mediante removeStickyEvent() o cuando se envía un nuevo sticky event del mismo tipo.
Sí, EventBus es thread-safe. Se puede llamar a post() desde cualquier hilo. La entrega de eventos a los suscriptores se sincroniza internamente. ThreadMode determina el hilo de ejecución del manejador: MAIN (hilo principal mediante Handler), POSTING (hilo de la llamada), BACKGROUND (cola de tareas en segundo plano), ASYNC (hilo separado). Para actualizaciones de UI, use MAIN. Para operaciones pesadas, use ASYNC.
Habilite el registro mediante EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Suscríbase a NoSubscriberEvent para rastrear eventos sin manejadores. Use SubscriberExceptionEvent para el manejo global de excepciones. Android Profiler ayuda a encontrar fugas. Para escenarios complejos, escriba una prueba: EventBus.getDefault().register(mock) + post(event) + verify(mock).
No, EventBus (GreenRobot) está vinculado a Android SDK y JVM. Para Kotlin Multiplatform, use Kotlin Multiplatform SharedFlow o KMMBus — bibliotecas que admiten código compartido. EventBus funciona en el lado Android de un proyecto KMM pero no está disponible en commonMain. Para eventos multiplataforma, prefiera mecanismos nativos de la plataforma o una abstracción mediante expect/actual.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también