Be aware of bus context issue
Bus (EventBus/Otto) is very flexible and powerful, yet could lead very big mistakes/misbehave as well.
When we first started to use bus approach, we were amazed to see how things could have been done so easily. After a while, we applied it every possible places to decouple classes.
Then we realized, something goes very wrong, the receivers could get any event from anywhere as long as it fits the event type.
We had the following structure. An activity which handles network request and deliver the response with events.
And a fragment which gets responses and consume them
Now we had the following scenarios:
We had a ViewPager and we used ProductListFragment for each page. When we run it, first and second pages make a request (due to viewpager offlimit) and they are both in resume state and activity makes 2 requests and gets 2 responses. We observed the following issues:
1when the response is delivered, both instance consumed it, so we made 2 request and we got 2 response and both instance consumed 2 response which was completely wrong behavior. Imagine you want to show different filters in each tab but because of this structure, they will be mixed.
2 One of them got failed and the other got the success response. Now both events are triggered again and both fragment consumed both events again. Imagine that you show a message to the user when it failed. First fragment was a success actually but instead got the error event from the second fragment and second fragment got the success event etc etc. Completely wrong behavior again.
3 We used endless list and pagination and each page has 20 limit for example. Now when you consume both success events, instead of showing 20, you will show 40… another misleading again.
And the worst is these bugs are very hard to find and analyze.
Examples could go on and on, so what we did to solve this problem?
We started to add a context identifier. Each event should be delivered to corresponding receiver and you can only do that by attaching the context of the receiver.
We used UUID and used it as a context identifier. It is guaranteed that it will be always unique. Every time, a caller request an event, they also pass their identifier.
When we get the response, we know exactly where to deliver this event, we also attach the unique id along with the data to the event.
And receiver just makes a check for the event and makes sure that it belongs to them. This approach looks a little code duplication but we couldn’t find a better approach.
Is it enough? Not yet.
We got a small issue for configuration changes situations such as screen rotation. Fragment could be recreated again but if it is recreated UUID will be changed but it shouldn’t. To avoid this, we saved the uuid in saved instance and restored it when the fragment is recreated. If the fragment is created for the first time, we generate a new unique id. That way we made sure that everything works properly.
As you may noticed, these things will be done for each fragment, then why don’t we put them in a base fragment? Thus we can avoid code duplication.
Now, we have a system which works stable. If you have any other approach to solve this problem, I’d love to hear it.