How to make Android more Rx – 5 tips & tricks
Table of contents
If you’re using RxJava in your daily routine, probably one day you’ll reach the point where you want to… put RxJava everywhere! Let me give you 5 practical pieces of advice.
Using RxJava everywhere is not a bad idea (usually). It’s normal that you’re thinking “Hey, it’ll look a lot cooler if I could just put this in RxJava flow, instead of using for-loop/Handler/Async Task/some other ugly solution”. Yes, it’ll be cool. If only I could…
Well, I have some good news for you – with RxJava you can do most of the things you’ve ever dreamed of! Let me give you my 5 pieces of advices about the things I like and often use in my projects.
NOTE
These code snippets will work both on RxJava and RxJava2. This article assumes you have some basic knowledge about RxJava. If not, I recommend you read some other articles first (this one on RxJava – the production line – makes for a great beginning).
#1 Double click detection
You have some buttons in your application and you want to detect if these buttons were clicked twice. Easy, isn’t it? Yes, all you need to do this is to make a counter, check for every click, move the counter up an increment and remember to reset the counter afterwards… No way, there must be a cleaner way to do this. Of course, here it is:
1 2 3 4 5 6 7 8 9 10 11 12 | private PublishSubject<Boolean> eventSubject = PublishSubject.create(); public static final int TIME_BETWEEN_EVENTS_MILLIS = 500; public static final int NUMBER_OF_EVENTS = 2; public DoubleClick() { eventSubject.buffer(eventSubject.debounce(TIME_BETWEEN_EVENTS_MILLIS, TimeUnit.MILLISECONDS)) .filter(events -> events.size() == NUMBER_OF_EVENTS) .subscribe(events -> doSomething()); button.setOnClickListener((button) -> eventSubject.onNext(true)); } |
By using PublishSubject
, we can emit our own items and catch them. After catching, we check if the number of events is the same as the value we want. Of course, you can do it with every event – getting a text from TextView, getting clicks from any other View or anything else.
#2 Repeating requests to API every 10 minutes
Using operator repeatWhen
, combined with delay
results, can result in delaying our subscription after some time. If, for some reason, you’d like to request for new data from the API every 10 minutes, you can do it with RxJava in just one line:
1 | sourceObservable.repeatWhen(completed -> completed.delay(10, TimeUnit.MINUTES)); |
Just keep in mind, that Observable
will only repeat if the previous one was completed.
#3 Combining two dependent objects
Let’s say we are receiving Cats from API, and, for every Cat, we’d like to get some Toys (which are dependent on Cat). After this, we want to have an object combined with CatAndToys – it’s possible to do this with RxJava using the second parameter of flatMap
(what is not so commonly used), which
[…] combines items from the source Observable with the Observable triggered by those source items, emitting these combinations.
Source: RxJava documentation
Let the code speak for itself:
1 2 3 4 | Observable<CatWithToys> catWithToysObservable = getCatObservable() .flatMap((cat) -> getToysForCatObservable(cat), (cat, toys) -> new CatWithToys(cat, toys)); |
We see this first parameter in flatMap
takes Cat as a parameter and returns Toys. Then the second parameter takes Cat and Toys (the result from the first function) and returns the CatWithToys object. This way, you can combine many dependent requests to API without using nested flatMap
operators.
#4 Request pagination
If you want to paginate through some objects from API, you can do it easily with RxJava. The trick is to merge all the pages until you achieve some final point (e.g. the last request gives you empty results, or the items count will be proper and correct). Let’s look at the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public Observable<List<User>> getAllUsers() { return getUsersObservable(null); } private Observable<List<User>> getUsersObservable(final String lastUserId) { return apiAdapter.getData(lastUserId) .filter(userList -> !isLastPage(userList)) .flatMap(this::getNextPageUsersObservable); } private Observable<List<User>> getNextPageUsersObservable(final List<User> userList) { Observable<List<User>> usersPageObservable = Observable.just(userList); int lastUserId = getLastUserId(userList); Observable<List<User>> nextUsersPageObservable = getUsersObservable(lastUserId); return Observable.merge(nextUsersPageObservable, usersPageObservable); } private boolean isLastPage(final List<User> userList) { return userList.isEmpty(); } |
Firstly, we’re requesting for a single page without any parameter (or you can put 0 here, depending on your API), then we check our end list condition – if we didn’t reach the end, we’re requesting for some new pages with the parameter from the last request (it could be the last ID of the user, or a date of the newest photo, or anything else) and we will merge it with the previous Observable
. If we reach the end of the list, we finish.
#5 Lightweight RxBus
If you’re using an event bus in your project (no matter if this is Otto, Event Bus from Greenrobot, or something else), you may be interested in implementing your own event bus – RxBus! You may be asking why we’re reinventing the wheel, instead of using something, that was made by someone else?
Well, when you see how easy implementing your own EventBus with RxJava is, you may rethink putting this in as an internal library. You’re independent of changes in the external library, so you can adjust RxBus to suit your needs and – if you already have RxJava in the project – why don’t you use it for something more?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class RxBus { private static final RxBus instance = new RxBus(); private final Subject<RxEvent> rxBus; public RxBus() { PublishSubject<RxEvent> rxEventPublishSubject = PublishSubject.create(); rxBus = rxEventPublishSubject.toSerialized(); } public static RxBus getInstance() { return instance; } public Observable<RxEvent> getRxBus() { return rxBus; } public void post(final RxEvent rxEvent) { rxBus.onNext(rxEvent); } public class RxEvent { } } |
The main “core” of RxJava is SerializedSubject
(very similar to PublishSubject
, which we used to detect double clicks, but this one is thread-safe). We simply create a singleton class that holds a reference to this SerializedSubject
. If we send some event, we emit items with SerializedSubject
, if we subscribe to the event, we subscribe to receiving events from SerializedSubject
. Simple, isn’t it? We use the .toSerialized()
method because we want to use our RxBus from different threads.
Conclusion
As you can see – there’s a lot of things you can do with RxJava. It’s helpful for views, data requesting and internal communication in your app. Remember, there are many solutions for the problems I presented and RxJava may not be always the best one, but I hope these tips inspired you to look for some other solutions for your issues.
That’s it! I hope you like my tips. Stay tuned for more!
About the author
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!