The Reactive Revolution: RxAndroid and how it finally clicked for me (part 1 of 2)
If you’re anything like me, you don’t truly understand something until you’ve had to use it for real. I guess it comes down to the way you struggle through the concepts to start with, learning from the little mistakes you make as you go, until you finally “get it”.
I’d been reading about the Android Reactive Extensions (henceforth “Rx”) stuff for a while, and had the idea it was somehow a new way to manage events in an app, in particular a sequence of them. How many times as a developer have we dealt with that? Whether we’re reading from a file, parsing the response to a REST call or just looping through some Views, this pattern is pretty fundamental, so anything which works with it sounds profound indeed. As it happens, I was working on an Android app which did exactly that and was in need of an update, so I decided to roll up my sleeves and react accordingly (ho ho).
I wanted to show a long-running progress bar which survived orientation changes, i.e. it continued where it left off. I knew there were several ways to achieve this such as a Loader, or maybe a Service with a Broadcast Receiver listening to the updates etc, but was currently using an AsyncTask. Anyone who’s done this kind of thing before will instantly warn of the danger of the dreaded Activity leak, which Alex Lockwood explains perfectly:
In Java, non-static anonymous classes hold an implicit reference to their enclosing class. If you’re not careful, storing this reference can result in the Activity being retained when it would otherwise be eligible for garbage collection.
To keep an eye on this I added LeakCanary, and I’ll be going over this issue as the last part of this article.
So I created a project to show the journey taken from this AsyncTask approach to a fully Rx one, from a known starting point onwards. Here’s how it looks, and in all cases the idea is to start the progress bar and counter, reorient the device and see them pick up where they left off — or not, as the case sometimes is, but that’s part of the journey and explained as we go.
There are seven ways to choose from in the spinner which will now be covered in order. Import the GitHub project for the full source, or where methods/classes are listed here which aren’t detailed, browse online.
1. Async Task
To preserve state across the orientation change a Retained Fragment is used. This has a few members which are used in the different techniques, the first of which is a custom AsyncTask:
The Retained Fragment is either created on first run or subsequently picked up in the MainActivities onCreate() method. The tricky part during the orientation change is handled in its onResume() callback:
The next way to do the same thing (;-)) was to create an IntentService and listen to its updates via a local Broadcast Receiver. Here’s the gist of it:
After the necessary Service registration in AndroidManifest.xml has been done, the nice thing about this approach is there’s nothing to worry about in the Activities onResume() since all we are doing is listening for updates. Its “busy” state is managed by the Retained Fragment.
Ok, so that’s two “regular” Android ways to do this which have been around for a while now, but what about Rx? The general pattern is to create an Observable object which emits a series of objects we are interested it, and to then create an Observer which acts upon each of these emitted objects. So for us, we need something which emits numbers — i.e. the progress values, which we’ll use Longs for. As it turns out, if we had a List of these numbers, and that’s “List” in the java collection sense, we can create this set up very easily along these lines:
The short cut here is the from(numbersList) method — it so happens from() can handle collections, or more accurately anything implementing the Iterable interface, which allows us to create the Observable sequence without needing a new fancy custom object to squirt out our numbers. However, running this shows a different problem now — it completes instantly, going straight from start to finish in the blink of an eye. What has happened is that although the contract for the Observable to be created has been satisfied, it hasn’t done so in a way useful to us — the numbers are counted through instantly.
4. Timed Emitter
Let’s use some more Rx magic to sort out the timing of our emitted numbers:
Running this looks better, and the wizardry is again in the various methods Observable gives us. First, we are using interval() to create the numbers directly from a 1 second timer. That will just keep on emitting them forever, unless we use another piece of magic, the take() method, to tell it how many to stop after. Finally these numbers are zero based, so we’ll use a simple map() lambda to increment them so they are ready to stick directly into the progress bar. Running this looks good and we see the counter move up as we’d expect. That is, until we rotate the device, at which point it starts again from the beginning no matter where we left off.
The reason for this is that we have created what’s known as a Cold Observable, which means it starts emitting from its initial condition each time a subscriber subscribes. That’s exactly what we do when we rotate the device — the onCreate() method resubscribes in order to show the progress updates, but the act of resubscribing restarts the sequence. That’s not the only problem here. In practice, we’re not going to be just creating numbers in sequence, we’re going to be doing useful work in our own class, such as iterating through a set of SQL records or whatever. To get any further, we need to stop using the inbuilt Observable mechanism for creating these numbers and use a custom class, which is exactly what will happen in part 2…