Clean Android Code articles series:

Basic idea of EventBus

In attempt to separate origin and handling of event, EventBus pattern have emerged in Android development community a few years ago.

Two libraries — EventBus by Green Robot and OttoBus by Square — became quite popular and sometimes used even now.

The basic idea of the pattern is that there is some kind of bus present in the application, that “transfers” events from one place of the application to another.

When you have an event that you would like to handle, for example, some button was clicked, or network connection became available,- you would post this event on a bus. Somewhere else in the application another class would be subscribed on a bus, listening for events going, and when the right event comes in — it will handle it. For example, by showing a SnackBar on the screen with a notification to the user.

Such approach lets you separate your layers, handling events in a separate class, independently from UI layer. Often, you would handle your events in a Presenter, and in the tests you would be able to simulate click event coming in and fully test the logic that handles that event.

Reactive approach

RxJava came to Android developers attention a few years later then EventBus, so even to this time many people have both RxJava and EventBus in their projects, still doing bus the old way.

There is no need for that anymore. If you are using RxJava — you can easily write a bus yourself.

Concept remains the same — some class subscribes, and another class posts something. The only difference would be that in Rx you will have your Observer somewhere subscribed, and another class would be posting onNext events.

Here is an example RxBus:

We are using PublishSubject from RxJava.

Subject is something that is both Observable and Subscriber at the same time, meaning it has onNext() method present, so we could publish data, and it can be subscribed to via subscribe(), like a regular Observable.

PublishSubject is a specific kind of Subject that delivers events as soon as they are posted.

And we also wrap it into SerializedSubject to make sure we would not have thread conflicts.

BusEvent is a parent class for all events. It could also be just SerializedSubject<Any, Any>, but I prefer to keep the bus clean. More on events structure a bit later.

Now, the concept of the bus itself is very nice, but if you are not careful enough it can very quickly become an anti-pattern.

Imagine a class that knows about all events that are going on in the application, and, at the same time, about all subscription entry-points. Such a class can easily become a God-object, knowing everything about everybody.

Another big problem arises when you have decoupled your events so much it starts to be difficult to understand which event goes where, especially when different kinds of events are mixed up on a bus.

For that reason, in my projects I prefer to have several busses.

GlobalBus is only one in the whole project. It can be used to communicate some global events, like connection becoming available, or our repository changing favorite state on one of the models.

GlobalBus will be exposed to ActivityScope, but should only be subscribed on in Presenters, carefully looking that it’s always unsubscribed afterwards so we wouldn’t get a memory leak.

ActivityBus, being in ActivityScope, would be re-created for every activity we put on the screen. It’s job is to pass events that are going from inner layers on the screen back to the root Presenter of the screen — if you really want events to be handled there. An example would be a click event, within an item, in the RecyclerView, on a ViewPage, in the ViewPager, in the Activity — it would be too much to pass this event via EventsDeleagate, right?

Speaking of EventsDelegate that are in EventsDelegatingViewExtension classes. You could use ActivityBus to get rid of it and just pass everything on the bus to be handled in the Presenter. I don’t do that because, in my opinion, ViewExtension becomes too decoupled from the UI layer, and, logically thinking, if there is nobody handling events, originating from ViewExtenion, it’s not 100% operable.

You can easily forget to handle some events if you would use EventBus, but never with EventsDelegate.

Also, EventsDelegate defines a very nice contract for every particular EventsDelegatingViewExtension, so it would be much easier to figure out what’s going on if you get back to the project 6–12 months later.

Now, let’s deal with subscription.

Bus Subscriptions

As I have mentioned, Presenters would be subscribing to the GlobalBus and ActivityBus, and they also need to unsubscribe on some point to ensure no memory is leaked.

For that I use a BusSubscriber class

A particular BusSubscriber needs to be injected into Presenter, subscribed and unsubscribed, usually in onCreate and onDestroy

//in OnCreate

globalBusSubscriber.subscribe(FavoriteChangedGlobalEvent::class) { event ->

refreshList()

} … //in onDestroy

globalBusSubscriber.unsubscribe()

Now, let’s clean up our events a little.

To have more information about event I usually separate events into several types:

UIEvent — some UI event, usually passed on ActivityBus, that you would like to pass in to your presenter, like a button pressed, or something else happening in the ViewExtension, for example

NavigationEvent — an event that should result in navigation — new activity will be started, or new fragment displayed. The reason to have this as a separate group — you would want have a single place to handle these types of events, most likely it would be a main presenter of current activity. Don’t try to handle these events in fragment presenters or other kinds of presenters — you would get a mixed navigation logic, that will be especially noticeable when you introduce ViewPages with Presenters.

GlobalEvents — events that are really global. Connectivity changed, your repository manipulated on data, and everybody needs to know about it.

And when you create a class for new event, add even type in the end of class name, like FavoriteChangedGlobalEvent or ShowGalleryNavigationEvent. That way you will see the type when subscribing for events and it will remind you that probably this particular event should be handled elsewhere.

Now, this is probably not the most robust system of bus events, but most likely the simplest. I am open to other suggestions and ideas on how to keep this clean.

Of course, we should not forget to write some tests:

As usual, relevant changes you can find on Github branch

The alternative?

In my article I have shown you how can you achieve decoupling with EventBus/RxBus, and pointed out how can you use it carefully so you wouldn’t have any issues. But I would still like to stress that EventBus can be abused and become an anti-pattern. If you are careful — you will most likely get no issues. But if a new guy comes to your team that doesn’t know that you try not to abuse the bus — you might get some problems on the way.

I find EventBus to be a nice pattern to solve some problems in the code, especially when some inner-view in the RecyclerView fires and event and somebody else needs to handle it. And, in my opinion, if you don’t abuse the bus — it’s all good.

But to cover all basis, let me give you some examples on what could be an alternative way of doing things. After all, we already have RxJava in the project, so why not just use the Observer pattern for events too directly, without a bus.

Instead of firing global events on a EventBus, like FavoriteStateChanged, for example, you can have a small “local bus” within FavoritesRepository itself — a local FavoriteStateSubject where you would post class-local FavoriteChanged events, and all parties interested would subscribe to FavoriteStateSubject.asObservable() for FavoritesRepository. With such approach you don’t have a bus to abuse, and you are still somewhat decoupled from event origin.

Similar pattern could be applied even to ConnectivityChangedEvents that come in as broadcasts. A ConnectionChecker would be injected into ConnectivityBroadcastReceiver and would learn about network state. Inside ConnectionChecker you would also have a ConnectionStateSubject for events, and interested parties would subscribe to those events via ConnectionStateSubject.asObservable(), to handle them.

A bit more difficult would be a case when you have event, originating in the RecyclerView item, that needs to be handled by parent screen, that holds the whole RecyclerView. Here I am open to community suggestions. So far the only alternative I see would be to pass some Observable inside through the chain on ViewPager-ViewPage-RecyclerViewAdapter-Item, but maybe there is a better way.

Clean Android Code articles series: