In this article I will describe one solution on a comments voting feature to prevent making too much network requests, while keeping your UI responsive.
Lately I’ve been implementing a Commenting feature in our Android app for the Daily Mirror Newspaper (link to Google Play), alongside with a Like/Dislike voting mechanism. Similarly to Reddit, you can vote for a
Typically when doing a network request on Android one has to:
- Start asynchronous work
- Wait for network response
- Update UI accordingly (success or error scenarios)
Literally following this pattern would make the UX a bit boring and so I followed an approach suggested by Yigit Boyar in this talk on designing offline-first applications. This can also be viewed as taking an optimistic approach on UX (https://uxplanet.org/optimistic-1000-34d9eefe4c05)
With this approach our UI updates its state immediately after user-clicks and the network requests are queued and performed at a later time. Network requests are expected to finish (most of the times) successfully with the help of Job Priority Queue library. When they don’t, the UI will simply revert gracefully to whatever it was before.
The problem we still had with this approach was that, in case the user starts spamming (pressing continuously) the likes/dislikes, the backend service for the voting system started to poop 💩💩💩 — it could not handle well all the requests happening quickly.
This is where we turned to RxJava for filtering unnecessary network requests. Assuming all
NONE clicks (aka
VoteAction) from all comments are handled in the same chain of events, this is how we handle it:
Get the last VoteAction per each comment
In the context of our problem, we only care about the last vote done by the user on each comment. Therefore assuming a continuous chain of
VoteActions , this translates to:
Buffer VoteActions on a fixed period of time
Returning the last VoteAction per comment already filters most unnecessary network requests, the last bit is to buffer all requests on a fixed period of time. This allows all events to be collected during some time and only be processed in batches.
By buffering all VoteActions, we’re able to batch the click events and only process them after a fixed period of time. Then, we only keep the last
VoteAction per each Comment. Finally, we apply a flatMap to transform the VoteAction into a “Job” that performs the actual network request.
The final solution took a bit of refactoring, to enable us to unit-test both operations. Here it is:
This is only one of many possible solutions, one that worked for us. Feel free to give some feedback in case you like it or if you find any inconsistency!