Async Android

RxJava is increasingly popular in the Android developer community. I was thinking back to the “ah ha!” moment when it’s usefulness really struck me. Even before getting to grips with RxJava’s functional aspects, it looked like a neat alternative to AsyncTask and Loader.

I’m going to explore Android’s threading abstractions and explain why RxJava can provide a more satisfying API for expressing asynchronous operations.

In the beginning

Let’s go right back to basics. All Android apps start with a single thread: the main thread (A.K.A “UI thread”). This is the thread we must use to make View updates, and it’s where we receive framework callbacks like onCreate().

Let the main thread do it’s thing

Executing long-running operations on the main thread is bad for performance and bad for users. It means dropped frames, janky animations and unresponsive interfaces. In the very worst cases, the system will show the user an ANR (Application Not Responding) dialog and ask if it should kill the app.

anr

We want to keep the main thread free to make UI updates as close to 60FPS as possible. This means that any slow or blocking operations like network access, database queries or intensive computations must be performed on background threads. The good news is that we’ve got options.

Thread

Creating a new thread is straightforward. Thread is the building block for every approach, but it’s not going to provide a great developer experience on it’s own.

It’s often necessary to react to the result of a background operation on the main thread. A result can be sent to the main thread with a Handler. In the example, Activity.runOnUiThread() simply posts to a main thread Handler internally. Either way, it’s not particularly elegant code.

thread

Creating a new thread for every single background operation isn’t ideal behaviour either. One option is to create a custom abstraction, perhaps sending a Runnable to a thread pool, but there are several more general-purpose tools available.

IntentService

IntentService is perfect for firing off background tasks when it’s not necessary to react to a result on the main thread directly.

It must be declared in the AndroidManifest.xml just like any other Service. Then it can then be started by sending an Intent (that can include any parameters).

startService(new Intent(this, BackgroundService.class));

An IntentService has it’s own worker thread. It operates serially, queuing subsequent invocations. It’s completely decoupled from the app UI, so there are none of the complexities that arise from an Activity being recreated (e.g. due to a configuration change) when a background operation is in progress.

On the other hand, this approach is not particularly well suited when a main thread update is required. It is possible to post a result to an event bus and listen from the UI, but that can become unwieldy as a general solution.

AsyncTask

Painless threading? Well, AsyncTask does have some shortcomings, but it’s the first of several abstractions with a clean separation between the code that runs in the background and the code that handles the result on the main thread.

The interface provides two important callbacks:

  • doInBackground() is executed on a worker thread
  • onPostExecute() receives the result on the main thread

It’s also possible to override onProgressUpdate() and receive progress events on the main thread.

AsyncTask works just fine in simple cases. So what’s the problem? We already touched on handling configuration changes, but that’s a common challenge for threading abstractions that live in the context of an Activity. There are three more concrete limitations.

Error handling

Operations that need to be executed on background threads tend to have a lot of failure cases – especially I/O. Network requests fail. Common patterns involve returning exceptions in result objects or storing them as state in the task. This means that we always need to check for error results (or null) and might end up dealing with less meaningful types.

Callback hell

Chaining multiple asynchronous operations together is complex and error-prone. Scenarios where multiple individual tasks depend on each other are not uncommon, though.

callbackhellChained or nested callbacks lead to “callback hell”. It becomes difficult to reason about the flow of control, and the possible failure of the whole chain must be handled in each callback.

Execution order

pools

This is just a piece of history, assuming a minimum API level 14+. The default threading behaviour of AsyncTask changed twice between platform versions. This resulted in some subtle bugs, inconsistencies and confused developers. It can be specified manually with executeOnExecutor().

Loader

Loader is an evolution of AsyncTask in many ways, and it shares some of the same limitations.

The key difference is that a Loader binds to a host Activity or Fragment. It allows background operations to continue and be restored relatively seamlessly across configuration changes. This is great. It solves a common and surprisingly difficult problem. However, being tightly bound to the framework often provides a separate set of challenges for unit testing.

There are cases where this might be the right tool for the job. For example, CursorLoader does quite a lot for free in combination with ContentProvider. I used it when I was building Sudoku, and it worked well, but that’s a relatively simple application.

In an ideal world

Thread, IntentService, AsyncTask and Loader all provide mechanisms for running code in background threads. They have some distinct characteristics and some shared limitations. So what would an ideal, general-purpose solution for expressing asynchronous operations on Android look like?

  • Main thread callbacks
  • Persistence across configuration changes
  • First class error handling
  • Composability (a solution to callback hell)
  • Simple, explicit control over threading behaviour

RxJava

RxJava is a library for the Observable abstraction and related higher-order functions. It allows us to meet 4/5 of the criteria above with little effort and it supports the functional-reactive programming paradigm in Java.

At this point, it’s fairly well battle-tested on Android. It’s been in production in the SoundCloud app for over two years and it’s used by several other companies like Trello, Square and The New York Times.

Observable

An Observable emits items. Any operation can be represented as a stream that:

  1. emits 0 or more items
  2. either completes or terminates with an error

Marble diagrams help to visualise the output of a stream over time.

create

Subscriber

A Subscriber consumes the output of an Observable. Subscribing to an Observable is essentially registering to receive items emitted from that stream.

  • onNext() receives items emitted by the Obserbable
  • onError() is a terminal event that receives a Throwable
  • onCompleted() indicates that the stream completed successfully

In addition to a separation of concerns between background and main thread work, there is a clean split between logic for handling success and failure cases.

Schedulers

Two important operators define the threading behaviour. In this example, the Observable should execute on a background thread, so it’s assigned to the I/O thread pool with subscribeOn(). The Subscriber callbacks update the UI, so they need to run on the main thread. This is defined with observeOn() and the Android main thread scheduler.

Composing streams

What about “callback hell”? This is where the real power of functional-reactive programming comes into play. RxJava provides an extensive set of operators for functional composition and transformation of streams. Multiple asynchronous operations can be combined with operators like flatMap().

flatmap

In general, these operators allow us to write more expressive, less stateful code. Many are familiar if you have exposure to functional programming.

Problem solved?

Kinda! We’ve seen how the Observable abstraction incorporates error handling, composability and scheduling (including the Android main thread scheduler).

comparison

However, the emoji-based feature comparison shows that RxJava (RxAndroid really) doesn’t provide an out-of-the-box solution for the configuration change problem. Operators like cache() and replay() can play a part, but the strategy for retaining a reference to an Observable will depend on individual architectures.

Some problems solved, some new problems

I want to share a few of the downsides and challenges, because nothing is perfect. These are the topics that seem to come up most often in conversation.

There is a learning curve. Incorporating a new programming paradigm requires you to think about problems in different ways. It can take some time to get familiar with the many useful (or sometimes just very complex) operators.

groupjoinThere is no “correct” way to use RxJava. It can solve a few different problems, so it’s worth reflecting on how it fits into your own architecture and problem-set. A threading abstraction? A tool for representing logic in streams? An event bus? UI bindings?

At some point, you may encounter a very Rx-specific issue called “backpressure”. This occurs when a stream is emitting more items than the subscriber can process. It’s necessary to be mindful of this constraint when using the main thread scheduler, since there is some thread switching overhead.

Currently, Android has no official support for lambda expressions, so function definitions involve a lot of boilerplate. The Retrolambda project backports Java 8 lambdas with Android support.

Conclusion

The default abstractions for expressing asynchronous operations on Android have several limitations in complex scenarios. RxJava provides a full-featured alternative with all the additional power of functional-reactive programming and a simple interface for handling results and errors. Replacing an old AsyncTask is an easy way to get started.


Tools
  • StrictMode: an essential tool for identifying long-running operations on the main thread
  • @UiThread / @WorkerThread: support annotations for static analysis of threading bugs
Links

SSL proxying with Charles

Charles is a great development tool for inspecting HTTP traffic. Here are the steps to enable SSL proxying with a Genymotion Android emulator and Charles 3.10:

  1. Proxy the emulator traffic to the host machine
    1. Long press “WiredSSID” and choose to modify the network
    2. Create a manual proxy with host 10.0.3.2 and port 8888
  2. Enable SSL proxying in Charles
    1. Proxy > SSL Proxying Settings… > Enable SSL Proxying
    2. Add hosts for the traffic you want to inspect
  3. Install the Charles certificate on the emulator
    1. Go to charlesproxy.com/getssl in the emulator browser
    2. Name the certificate and select Wi-Fi credential use

A PIN lockscreen is required to install the certificate in 3.1. Note that 10.0.3.2 is a Genymotion-specific IP for the host machine. Setting up a physical device is similar. Just make sure the machine running Charles is on the same network and use it’s IP address in 1.2.

android-crop 1.0.0

Last week I published version 1.0.0 of android-crop – a simple image cropping library for Android.

This library began as a replacement for a fork of an unmaintained project that we were using at SoundCloud. It hadn’t been getting much love, and most importantly for me, the UI looked really dated.

Image cropping is not a major feature in our app, but it’s a useful part of the recording and sign-up flows. Leaning on a library for this functionality keeps our project focused.

The library aims to provide:

  • Gradle build
  • Modern UI
  • Simple builder interface for crop Intent
  • Example project

The following snippet starts a crop, loading the image in the library UI and saving the cropped version to the specified URI. This example also includes an aspect ratio option, which specifies that the output should be square.

Crop.of(inputUri, outputUri)
        .asSquare()
        .start(activity);

android-crop

The image cropping logic itself comes from the AOSP camera code and is largely unchanged. Find the full source and documentation on GitHub.

Scaling up

Part 5

Awkward full-width list items are a common problem when layouts designed for phones screens are scaled up to tablet sizes. Multi-pane layouts and margins are a couple of simple solutions.

I even took a shortcut on the new multi-pane level selection. It’s just a separate PageIndicator implementation for wide screen layouts. On phones you get tabs. On landscape tablets you get a list.

twopane

Other screens have been modified with margins when they extend beyond 620dp in width.

margins

These tablet optimisations tie in nicely with the Google Play Games cloud save integration. Users can now split their game activity between multiple devices, with a UI optimised for each, and without ever losing their game progress.

Beta

The 1.3 release is almost ready, so this post concludes the series. Head over to the beta community to try it out!

Designing consciously

Part 4

It would be fair to call the old scoreboard screen “engineer design”. All the right information was there, but it didn’t provide much value. It hadn’t been designed from the user perspective. It was text heavy. It was awkwardly balanced. It displayed empty cards for levels that had never been played.

scoreboard_before

The card paradigm makes sense here, but the content needed to be rethought. The screen also lacked an overview of progress. I sketched out some new card ideas to answer questions like: How many games have I played? What level were they? What’s my best time?

redesign

A new summary card provides a helicopter view of progress in the game, with the total number of completed puzzles. The graph animates as you land on the screen, visualising the level distribution of those puzzles.

scoreboard_after

This screen is now home to the Google Play Games features too, making it the single location for progress and competition.

The score cards themselves mirror the summary card, displaying the total completed games for each level. The stats are close to their descriptions, making it easy to extract the best and average times at a glance.

The illusion of integration

Part 3

The padlock icon on this in-app purchase list item was intended to reflect the puzzle pack state, but it often caused confusion. Sometimes a purchase would fail and the user would ask: Is the pack locked? Do I need to unlock it to buy it? 

packs_before

The alternative was to mimic the buy button style from Google Play, giving us the illusion of tighter integration. Now the purchase flow feels like it starts with puzzle pack selection, rather than in the subsequent pop-up dialog.

packs_after

The resulting layout is cleaner and more familiar. It removes a source of confusion in the worst possible place. It’s a neat example of a high impact change from just a couple of small layout updates.

Ditching old deserts

Part 2

The next update of Sudoku will be the first to target Android 4.0 and above. This means that 17.5% of the current user base will not get the option to upgrade, but it also means faster iteration in a cleaner codebase.

The analytics data suggests the Android 2.3 users are disproportionally less engaged with the game, producing only about 10% of sessions. The return on investing that extra ~30% (gut feeling) of development time in backwards compatibility was dwindling. Moving to minSdkVersion=”14″ allowed dropping several compatibility dependencies and provides more opportunities to use newer APIs.

total_installs_pie

The experience for the pre-ICS user is not that bad either. They’ll continue to be able to use the current, stable version of the app.  Around 12% of new installs on any given day are Android 2.3. Those users may never get the latest features, but the other 88% will get them a whole lot sooner.

Adding and removing

Part 1

This begins a series of posts following progress towards the next update of my sudoku game. Version 1.3 will bring Google Play Games integration and a series of UX improvements. I’m going to write about the latter here.

Simplicity is at the core of this particular sudoku game. I receive a lot of feature requests, but only pursue ones that align with this goal: simple, beautiful sudoku for Android. The next release features two often-requested enhancements. Completed games can be filtered out from puzzle selection. ‘Solved’ digits are automatically disabled during gameplay.

hide_completed

I also spent time examining the performance of existing features to see what wasn’t being used and what didn’t quite fit. Two features have been cut. Analytics showed that a setting to turn off conflict highlighting was almost never used. The solver screen was also removed. It always felt a bit like clutter, so it will continue as a separate app.

This is first time I’ve really planned a release of the game. A little analysis of the feature set really helped me to focus on the important bits and keep an eye on creep.

One-shot banners

There’s not much more frustrating than a modal “Rate this app!” dialog minutes after installing a new app.

It would be nice to invite users to do that at some appropriate moment, though. Only if they want to. It needs to be easy to ignore. It needs to be easy to make it go away forever.

I’ve been experimenting with what I’ll call the one-shot banner pattern in Sudoku.

A borrowed idea

We ran a private beta when I was working on the Songkick Android app. The most common piece of feedback by far was that existing users wanted to log in straight away and couldn’t find the option. (It typically presents itself when you take an action that requires an account.)

songkick_login

We sketched out a few different options: dialog, notification and banner. The banner won. It sits within the context of the app, it’s immediately visible without being jarring and it’s friendly to users that don’t want to take either action yet.

Current implementation

The version in Sudoku evolved a little. It has more in common with the action bar, sharing a consistent height and action pressed states. It’s also slightly transparent to hint that it’s a transient feature of the UI.

In practical terms, this is just a Fragment added to the bottom of the Activity layout. Completing the action or pressing the dismiss button will remove the Fragment and SharedPreferences are used to maintain the shown state. All quite simple.

banner_plus

The user is invited to +1 the game after 2 completed puzzles. This is just pressing a button so it’s low commitment.

banner_rate

I want to ask the user to rate the game once they’ve played long enough to have formed an opinion. (Hopefully the fact that they’re still playing means they like it.) This action takes the user out of the app to Google Play, which is potentially disruptive, so it’s shown after 5 completed puzzles.

banner_store

After 10 puzzles, the user probably knows what difficulty they like to play. They might even be running low on puzzles of a particular level. This is a good time hint that there are more available.

Click-through rates

Roughly 3.3% of users presented with the rating banner click through. This suggests that it’s serving a purpose. It also makes a strong case against doing something modal. The vast majority of users aren’t interested in rating the app even though they’re highly engaged.

Resources

Stefano Dacchille wrote a great blog post on holding off asking users to rate an app if they’ve experienced a crash recently.