Xebia Background Header Wave

There’s an intersting low-level class in the Android SDK called Looper. Each app has at least one, because it powers the UI Thread. You can create Android apps perfectly fine without ever using Looper yourself, but it’s an interesting thing, so let’s take a look under the bonnet.

Block diagram showing Looper, Message Queue, three Messages and two Handlers.

Structural overview of Android’s Looper API.

The visible building blocks are Looper, Handler and Message. Behind the scenes, there’s MessageQueue. Each Looper is tied to a single Thread, and each Thread has at most one Looper. The Looper is accessed primarily through the Handler class.

Handler has the methods through which you obtain message instances and submit them to the message queue of the associated looper. Once your message is at the front of the queue, the Handler is also the object to process that message. You can implement the message processing behaviour either by subclassing Handler and overriding the handleMessage(..) method, or by creating the Handler with an implementation of the Handler.Callback interface.

Messages have five public fields. The int what defines the message type. It allows a single Handler (or Handler Callback) to handle multiple kinds of messages using a switch construct. The two ints arg1 and arg2 and the object obj are free format arguments that you can use however you see fit. The replyTo field is outside the scope of this blog post.

What about ExecutorService?

Java 1.5 introduced the util.concurrent package and, with it, the Executor framework. This framework is included in the Android SDK and the Looper is similar in purpose to a SingleThreadExcecutor, albeit clearly less versatile: you can’t use Callable‘s and Futures, you can’t change your mind about the chosen ExecutorService implementation or tune the queue length.

You might therefore ask “Why reinvent the wheel?”.

The reason is that Looper has a different performance profile. Recall that the UI thread is powered by a Looper. What you can’t tell from the diagram shown earlier is that the Message objects are kept in an object pool and used over and over. This means that, if you choose the message payload right, a looper can run indefinitely without creating garbage collector load.

The gold standard for a UI is to refresh sixty times per second. This gives the user the smoothest scrolling. Had the UI Thread been built on top of an executor service, the garbage collector load would have made such a refresh rate impossible and the user wouldn’t have been scrolling smoothly for very far.

Putting Looper to use

The most common use of the Looper API is as a means to hop onto the main thread from a background thread. This is how the onProgress() and onPostExecute() methods of AsyncTask are implemented.

[sourcecode language="java"]
class HandlerSample implements Handler.Callback {
private final Handler mHandler = new Handler( Looper.getMainLooper(), this );
private static final int MSG_TYPE_A = 1;
private static final int MSG_TYPE_B = 2;
/* E.g. a sensor callback or an IntentService. /
void someMethodThatRunsInTheBackground() {
// Background processing goes here…
// The obtainMessage() and sendMessage() methods are thread-safe and can
// be called without external synchronization.
Message msg = mHandler.obtainMessage( MSG_TYPE_A, resultObj );
mHandler.sendMessage( msg );
}
/* Invoked on the UI Thread, by means of Looper.getMainLooper(). /
@Override public void handleMessage( Message msg ) {
switch (msg.what) {
case MSG_TYPE_A:
// Processing goes here
break;
case MSG_TYPE_B:
// Processing goes here
break;
}
}
}
[/sourcecode]

Incidentally, if you ever need to check whether the current thread is the Main thread, simly test ( Looper.myLooper() == Looper.getMainLooper() ). This can be a useful complement to StrictMode.

Using your own Looper

Besides the main looper, it’s perfectly possible to create your own Looper and do message processing in a background thread. By nature, this is a low-level construct and not all apps need it. Peripheral I/O is a good candidate for handling in this manner, for example, talking Bluetooth to an Arduino.

To begin using a looper, you need to call Looper.prepare() and Looper.loop() on the Thread that will do the looping. The call to loop() doesn’t return; from the moment you call this method, your thread is processing messages until the quit() method is called on its Looper.

[sourcecode language="java"]
class BackgroundLooper extends Thread implements Handler.Callback {
/* Careful: this field will be null for some time after start(). /
public Handler handler;
@Override public void run() {
Looper.prepare();
handler = new Handler( Looper.myLooper(), this );
Looper.loop();
}
@Override public void handleMessage(Message msg) { // }
}
[/sourcecode]

The SDK includes HandlerThread to take the boiler plate off your hands and make all this a bit easier.

Remarks

  • When using Handler, make sure you enqueue messages with the one of the send() methods and not with the dispatchMessage(..) method. The latter comes first in an alphabetically sorted autocompletion pop-up, and you wouldn’t be the first person to call the wrong method ;).
  • If you create a Handler with a constructor that doesn’t take a Looper argument it will use the current thread’s Looper, throwing an Exception if there is none. I think it’s better to be explicit, and always use an initializing constructor with either Looper.getMainLooper() or Looper.myLooper() as an argument.
  • The MessageQueue is seldom used directly, but you can get it from the Looper to add an IdleHandler to it.
  • Be careful when you hold on to managed objects such as Activities and Views in a background service. Just like with AsyncTask, you can get into all kinds of trouble if you refer to a UI element that is no longer valid.
  • When you call Looper.quit(), the looper exits as soon as it can. It finishes processing the current message, but it doesn’t process any other messages left in its queue. If you want the looper to finish processing the remaining messages, put a “poison pill” message on the queue that tells your Handler to quit() its looper.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts