The Reactive Revolution: RxAndroid and how it finally clicked for me (part 2 of 2)
In the first part of this article, we explored several ways to show a long running determinate progress indicator using the “traditional” ways in Android. This focused on keeping things snappy by doing the work in the background whilst updating the UI with its progress, and ended up by starting to do the same thing but using Rx. Now we’ll address a few of the shortcomings we encountered and refine things further.
5. Cold Observable
Let’s see how the Cold Observable (our custom class to emit these numbers) will be created — it’s not that different to the earlier way of doing it:
It can be seen here why the sequence restarts each time a subscriber subscribes — the OnSubscribe() method has told it to do just that! In all other respects, this is pretty much the same as the Timed Emitter except we now have the “hooks” needed to do our own work in this class.
We stop listening to this and all the “Rx” techniques in the Activities onPause() method:
If we didn’t do this we’d leak the Activity. One of the strengths of RxAndroid is the way it can hook into the Activity lifecycle this way, and there are further helpers such as Trellos RxLifecycle library.
6. Custom Collection
We’re getting nearer our goal now. We know we need to stop the sequence from restarting each rotation, and we need a custom class to do our real work in. As it happens, the term we’re looking for is a “Hot Observable”. That’s one which continues on regardless of any subscribers. It would begin, then start counting and if we rotated the device we’d unsubscribe and resubscribe when the new Activity was created, but the sequence would be unaffected. The way this is done is via a Subject, which is a little like a pipe which can connect Observables to Observers because it behaves as both. Our existing Subscriber will subscribe to it, and it in turn will subscribe and unsubscribe from the Observable. In this regard, it turns the Cold Observable into a Hot one.
We’ve another problem — we need to create an Observable which doesn’t depend on any of the earlier tricks to control its timing, such as in the Cold Observable where we used the create() method to do it. Recall that the from() method takes a class which implements the Iterable interface. So once we’ve moved the “emitted object” generating logic away from create() , we need to create a Custom class which has a collection of numbers it can emit and implements this interface:
The Observer for this is created like this:
Running this gives us … the same as the List! It immediately completes, and you can see its for the same reason — we have no timing delays to slow it down. We need to refine the Custom class.
7. Custom Iterator
We’ll refine the Custom Collection so it has a Custom Iterator:
So although the Custom Collection did provide the Observables from() method with a class which implemented the Iterable interface, that class merely returned the same interface from its internal List. To be really useful, and get our timing logic in place (or in reality other useful work), we need to go the whole hog and provide a custom iterator as we do here. Running this moves the progress bar nicely, and on rotation picks up where we left off because the Activities onResume() method resubscribes to the Observable we stored in the Retained Fragment via our Subject.
Dealing with leaks
I mentioned at the start there is a concern with leaks here — in particular, the Activity. Recall that when using Rx Observables this way, threads are created to do the work. These threads are anonymous and are inner classes to the Activity. When they start, they run in the Apps process, and survive an orientation change when the Activity is destroyed and recreated. However, the thread that first Activity created will still have a reference to its Activity and therefore not be eligible for GC at that point. The new Activity won’t create a new thread, it will just subscribe to any existing ones, so it and any subsequent Activities will be cleaned up normally. Only when the first running thread terminates can the first Activity it was created in be GC’d. Here’s the memory graph:
The top one shows multiple orientation rotations whilst the Custom Iterator was running, the bottom one was after the thread terminated.
This is ultimately not a problem because it does clear in the end. When the first Activity is destroyed and the thread it created is still running, Leak Canary will get suspicious because it will see the Activity hasn’t been GC’d. In fact all it does is create a heap dump where it “suspects” there could be a leak, and knows it needs to analyse it further. So at worst, you have the first Activity suffering a delay before being GC’d whilst its spawned thread completes — and that’s what we’re seeing in the second graph. Things would not be so rosy if an Android Rx Observable risked permanently leaking an Activity every time it used one!
In summing up, I found it fascinating to apply these reactive techniques to familiar patterns, and am convinced the simplicity and power of the threading model is a perfect fit for modern mobile apps. This is because they are becoming more and more complex, and you can’t really predict what users will do with your app but they are still demanding it does more, and all at once.
As you become familiar to the idea of reacting to a sequence of emitted objects, which is the essence of what Rx is all about, things start to get really interesting. You start to think how it can be applied to something you’re currently working with using “traditional” iteration methods, and when applied can often throw up new and exciting ideas which are much easier to implement. And that’s something I’d certainly call a positive reaction!