The problem

Many of you would have already build a screen like this one in the left: a nice ViewPager with few tabs (usually represented by Fragments).

So let’s imagine now that we’re building a News app and we have 2 tabs: one with a list of articles and the other with the user’s favorite ones. Apart from bookmarking this articles, we also want the user to be able to like them.

So we have 2 social interactions (bookmark and like) and 2 Fragments with a list of articles in one and a subset of those articles in the other.

So let’s say an user now bookmarks the first article in the first fragment; it should appear in the second one. What happens now if the user also likes the first article either in the first or second fragment? What happens is that we need to propagate this change to any fragment that contains articles, so the social interaction changes so, how to build this?

Of course we’re in 2016 and the fever for Rx is noticeable, and I’m not gonna lie: it would be extremely easy to solve the problem using Rx (as Dionysis Lorentzos demonstrated in this article). But at that point we weren’t using it in the project and including the lib wasn’t an option due to the complicated learning curve.

So the first solution that probably came to your mind is just using Listeners: since this 2 Fragments are contained in the same Activity let’s just make them implement a beautiful “OnArticleUpdatedListener” to react on this changes. But now you also need a way to broadcast this change, so of course you need to do the opposite thing: let the Activity implement an interface so the Fragments can tell it that the Article changed, right? Well the problem of this approach is that as soon as your app grows, this can become something really really monstrous, so at the end our beloved listeners are not an option after all.

I guess I don’t need to tell you that this was exactly what happened to me in my company, so I didn’t want to wait too long and decided to change the approach.

EventBus to the rescue!

So then I examinated different Bus-based solutions (otto, EventBus, AutoBus) and decided to try EventBus.

So as opposed to the previous architecture in which our Fragments were really coupled with our Activity, now EventBus offered us this nice picture:

The apparent beauty of decoupling listeners

Nothing is tied! Just get the EventBus instance and register/post events to it. You could even select the thread in which the events would be received, great! What happened then is that we built our app, we included EventBus and everything was working perfectly, easy and really quick to implement: whenever the user interacts with the article we fire an event telling that the article has changed and including it inside so the receiver doesn’t need to fetch the backend nor even the database, cool!

And then at some point we added another Activity… But you might think: “It doesn’t matter, right? EventBus will still be working as before”, well not exactly.

We added another activity which also interacted with our articles, so if the user goes there, interacts with an article and goes back we need to update our 2 fragments. There are actually many problems here…

Our 2 Fragments might be detached from the parent Activity and, therefore, we cannot just fire an event like before, first we need to make sure that our Fragments get unregistered from EB in onStop(). So to fix this we used postSticky() and what this does is to post an event that will stay there until somebody removes it manually. Problem solved (for now).

Ok, so now when the user comes back from this new Activity after interacting with an article, we get a sticky event and just right after our Fragments are registered in onStart() we got our event. But now this sticky event is still there because nobody has removed it from the EventBus, so if we rotate the screen, or we go to the home screen and return, we will get that annoying old event over and over again. I though a lot on how to fix this behavior and still today is something that bothers me but there wasn’t any good solution (if you know EventBus you could suggest to use priorities and to put a receiver with a higher priority that deletes the event, but then you’ll need to remember that behavior in case your app changes or you remove that receiver). At that point I was convinced that EventBus was still the best option so what I did (and I still regret that decision) is to let the parents activities get all the events of the children and put them as the result in case it was called with startActivityForResult() and also add a onActivityResult() that launches all the events after the creation of it’s Fragments, awful.

So after dealing with this problems everything seem to be working again, nice. At some point another person started working with me and he really liked the EventBus approach and actually he though that we could use it for more: like replacing a normal callback when requesting some data from the backend… Something was bugging me with that idea but I couldn’t find what exactly, so I let him try it just for one call to see how it could work.

As you might imagine it worked quite bad and the main reason for this is the same as for the entire Bus approach: that you completely loose track of the calls.

Consequences & lessons learned

Pros

You can effectively decouple your Fragments and Activities and get rid of really complex listener-based architectures.

The ability to change the receiver thread could be quite useful, specially on Android.

Is easy to pass small objects in the events for quick UI updates.

Cons

Is really easy to abuse it and make your code unreadable and untestable.

You need to fill your code with as many onEvent methods as receivers you want, making it less easy to understand for new people.

You loss control over who’s notifying who unless you use external plugins.

Next Steps

Since we implemented RxJava in the project few weeks ago I would like to try to implement our own RxBus version to avoid some of the cons above explained.

The main lesson I learned so far is that there’s no silver bullet and is really easy to abuse of a library like EventBus and make your project worse in the process. So just because it works doesn’t mean that you have to use it everywhere! Please remember to analyze every problem within its context and adopt the best solution, not the most trendy.