Building responsive apps with Handler

The Handler class is fundamental to the infrastructure of Android apps—together with Looper. It underpins everything that the main thread does—including the invocation of the Activity lifecycle methods.

While Looper takes care of dispatching work on its message-loop thread, Handler provides the means to add work to the message queue belonging to a Looper.

We can create a Handler to submit work to be processed on the main thread simply by creating a new instance of Handler from an Activity lifecycle method such as onCreate, shown as follows:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Handler handler = new Handler();
    // …
}

We could also be explicit that we want to submit work to the main thread by passing a reference to the main Looper instance into the Handler constructor.

Handler handler = new Handler(Looper.getMainLooper());

Exactly what we mean by "work" can be described by subclasses of java.lang.Runnable, or instances of android.os.Message. We can post Runnables to a Handler instance or send Messages to it, and it will add them to the queue belonging to the associated Looper instance.

We can post work to a Handler very simply, for example, by creating an anonymous inner Runnable:

The Looper instance to which the Handler is bound works its way through the queue, executing each Runnable as soon as possible. Posting with the post method simply adds a new Runnable at the end of the queue.

If we want our Runnable to take priority over anything currently in the queue, we can post it to the front of the queue ahead of existing work:

In a single-threaded application it might seem like there is not a whole lot to be gained from posting work to the main thread like this, but breaking things down into small tasks that can be interleaved and potentially reordered is very useful for maintaining responsiveness.

This becomes clear if we consider the question: what if we want to schedule some work in 10 seconds time? One way to do this would be to Thread.sleep the main thread for 10 seconds, but that would mean the main thread can't do anything else in the meantime. Also, remember that we must never block the main thread! The alternative is to post a delayed Runnable to the Handler.

We can still post additional work for execution in the meantime, and our delayed Runnable instance will execute after the specified delay. Note that we're using the TimeUnit class from the java.lang.concurrent package to convert seconds to milliseconds.

A further scheduling option for posted work is postAtTime, which schedules Runnable to execute at a particular time relative to the system uptime (how long it has been since the system booted):

Since postDelayed is implemented in terms of an offset from the SystemClock uptime, it is usually easier to just use postDelayed.

Since we instantiated our Handler on the main thread, all work submitted to it executes on the main thread. This means that we must not submit long running operations to this particular Handler, but we can safely interact with the user interface:

This applies regardless of which thread posts the Runnable, which makes Handler an ideal way to send the results of work performed by other threads to the main thread.

Handler is so fundamental that it is integrated right into the View class hierarchy, so we can rewrite the last example as follows:

For comparison, both of the previous examples are roughly equivalent to the following AsyncTask code:

When writing code in an Activity class, there is an alternative way of executing a Runnable on the main thread using the runOnUiThread method of Activity. If invoked from a background thread, the Runnable will be posted to a Handler instance attached to the main thread. If invoked from the main thread, the Runnable will be executed immediately.

When we post a Runnable, we can—as seen in the previous examples—define the work at the call site with an anonymous Runnable. As such, the Handler does not know in advance what kind of work it might be asked to perform.

If we often need to perform the same work from different call sites we could define a static or top-level Runnable class that we can instantiate from different call sites.

Alternatively, we can turn the approach on its head by sending Messages to a Handler, and defining the Handler to react appropriately to different Messages.

Taking a simple example, let's say we want our Handler to display hello or goodbye, depending on what type of Message it receives. To do that, we'll extend Handler and override its handleMessage method.

Here we've implemented the handleMessage method to expect messages with two different what values and react accordingly.

To attach an instance of our Handler to the main thread, we simply instantiate it from any method which runs on the main thread:

Remember that we can send Messages to this Handler from any thread, and they will be processed by the main thread. We send Messages to our Handler as shown:

Messages may also carry an object payload as context for the execution of the message. Let's extend our example to allow our Handler to say any word that the message sender wants:

Within our handleMessage method, we can access the payload of the Message directly by accessing the public obj property. The Message payload can be set easily via alternative static obtain methods.

While it should be quite clear what this code is doing, you might be wondering why we didn't create a new instance of Message by invoking its constructor, and instead invoked its static method obtain.

The reason is efficiency. Messages are used only briefly—we instantiate, dispatch, handle, and then discard them. So if we create new instances for each we are creating work for the garbage collector.

Garbage collection is expensive, and the Android platform goes out of its way to minimize object allocation whenever it can. While we can instantiate a new Message object if we wish, the recommended approach is to obtain one, which re-uses Message instances from a pool and cuts down on garbage collection overhead.

Just as we can schedule Runnables with the variants of the post method, we can schedule Messages with variants of send.

There are also empty-message variants for convenience, when we don't have a payload.

So far we subclassed Handler to override its handleMessage method, but that isn't our only option. We can prefer composition over inheritance by passing an instance of Handler.Callback during Handler construction.

Notice that the signature of handleMessage is slightly different here—we must return a boolean indicating whether or not the Message was handled. To create a Handler that uses our Callback, simply pass the Callback during Handler construction.

If we return false from the handleMessage method of our Callback, the Handler will invoke its own handleMessage method, so we could choose to use a combination of inheritance and composition to implement default behavior in a Handler subclass, and mix in special behavior by passing in an instance of Handler.Callback.

With SpeakHandler set up like this, we can easily send Messages from any thread to update the user interface. Sending from a background thread or the main thread itself is exactly the same—just obtain a Message and send it via SpeakHandler.

Let's extend our example to bind the app to a network socket and echo lines of text it receives from the socket to the screen. Listening for lines of text from the network socket is a blocking operation, so we must not do it from the main thread.

We'll start a background thread, then bind the server socket and wait for a client to connect over the network. When this background thread receives text from the socket, it will send it in a Message to the SpeakHandler instance, which will update the user interface on the main thread.

Let's have a quick look at the key elements of the run method. First, we bind a socket to port 4444 (any port higher than 1024 will do) on the given address, which allows us to listen for incoming connections. The second parameter says we're only allowing one connection at a time:

We loop forever, or until an exception is thrown. Inside the loop we wait for a client to connect:

The accept method blocks, so our background thread is suspended until a client makes a connection. As soon as a client connects, we send a SAY_HELLO message to our Handler, so the main thread will update the user interface for us:

Next, we wrap a buffering reader around our socket's input stream, and loop on its readLine method, which blocks until a line of text is available.

When we receive some text from the socket, we send it in a Message to our Handler.

If we receive bye, we'll break out of the loop, disconnect this client, and say goodbye:

To obtain the address of the device, we can use the Wi-Fi service. We'll need to convert the value we get from the Wi-Fi service to an instance of the InetAddress class, elided here for brevity.

Binding the socket and using the Wi-Fi service requires permissions to be requested in the Android manifest:

Finally we need to create and start Parrot in a suitable Activity lifecycle method, for example, onResume. To make it easy to connect, we'll display the address and port on the screen.

We should also remove pending messages in onPause, and disconnect the socket if the Activity is finishing—the complete code is available for download from the accompanying website.

When the app starts, you'll see a message on the device screen like telnet 192.168.0.4 4444. To connect and send messages to your device's screen, open the command prompt on your development computer and copy the text from your device screen to the command prompt.

You should see the following output:

Congratulations! You're connected. Enter some words in the command prompt and they'll appear on your device screen. Enter bye to disconnect.

It is worth spending a few moments to consider the difference between posting Runnables and sending Messages.

The runtime difference mostly comes down to efficiency. Creating new instances of Runnable each time we want our Handler to do something adds garbage collection overhead, while sending messages re-uses Message instances, which are sourced from an application-wide pool.

The difference at development time is between allowing code at the call site to specify arbitrary work for the Handler to do (Runnable.run) potentially spreading similar code throughout the codebase, versus the Handler defining the work it is prepared to do in a single place (Handler.handleMessage).

For prototyping and small one-offs, posting Runnables is quick and easy, while the advantages of sending Messages tend to grow with the size of the application. It should be said that Message sending is more "The Android Way", and is used throughout the platform to keep garbage to a minimum and apps running smoothly.

So far we only really considered Handler as a way to request work be performed on the main thread—we've submitted work from the main thread to itself, and from a background thread to the main thread.

In fact we can bind Handlers to any thread we create, and in doing so allow any thread to submit work for another thread to execute. For example, we can submit work from the main thread to a background thread or from one background thread to another.

We saw one way of setting up a Looper thread with a Handler in the Understanding Looper section earlier in this chapter, but there's an easier way using a class provided by the SDK for exactly this purpose, android.os.HandlerThread.

When we create a HandlerThread, we specify two things: a name for the thread, which can be helpful when debugging; and its priority, which must be selected from the set of static values in the android.os.Process class.

HandlerThread extends java.lang.Thread, and we must start() it before it will actually begin processing its queue:

Now we need a Handler through which we can pass work to our HandlerThread. So we create a new instance, but this time we parameterize the constructor with the Looper associated with our HandlerThread.

That's all there is to it—we can now post Runnables for our HandlerThread to execute:

To send Messages to our HandlerThread, we'll need to override handleMessage or provide a Callback via the alternate Handler constructor:

If we create a HandlerThread to do background work for a specific Activity, we will want to tie the HandlerThread's lifecycle closely to that of the Activity to prevent resource leaks.

A HandlerThread can be shut down by invoking quit, which will stop the HandlerThread from processing any more work from its queue. A quitSafely method was added at API level 18, which causes the HandlerThread to process all remaining tasks before shutting down. Once a HandlerThread has been told to shut down, it will not accept any further tasks.

Just as we did in the previous chapter with AsyncTask, we can use the Activity lifecycle methods to determine when we should quit the HandlerThread.