Advertisements

A friendly introduction to Handler, Looper and MessageQueue.

In this article we are going to dive into a very powerful Android API which is sometimes a bit harsh to grasp for beginners, the Handler. We will see what handler is and how it relates to Looper and MessageQueue. Starting from a basic definition of each of these classes we will then move on to some practical examples to see how they work together to build the foundations of the Android threading model.


When we use Handler we are really using an entire framework of communication, it is a powerful Android API that enables programmers to

    • Send and receive messages inside a thread or between different threads.
    • Post runnable objects inside a thread or between different threads.
    • Schedule messages on a specific thread at a specific time, or after a delay.
    • Schedule runnable on a specific thread at a specific time, or after a delay.
    • Cancel messages runnable objects so they won’t be delivered.

Handler is indeed very powerful, every Android developer should have a basic understanding of how it works. A good part of the Android framework relies heavily on the Handler to deliver events inside applications and core system components.

What is an Handler?

Handler is a communication channel. From one end of the channel we send a message and at the other end we receive that message. What makes Handler so interesting is that it can cross thread boundaries. From one thread we can send a message to a different thread. This is one common use case, however it is also very common to use Handler to send messages to the current thread. The UI thread does this all the time. We can also send a runnable object through an Handler, this operation is known as post. There is an important difference between sending a message and posting a runnable object

  • When sending a message we send information, namely a message type and a bunch of other data that the receiver can use. This means that there must be something on the other end of the communication channel to receive the message and react to it.
  • When posting a runnable object we send behaviour, meaning code. This does not require a receiver to react. What should happen when the thread receives the runnable is encoded inside the runnable itself. The idea of sending code might still sound confusing at first but we will soon see how straightforward this is.

Posting a runnable object simply means posting a very specific type of message that includes the runnable, therefore from now on I when I say “sending messages”, remember that this also includes posting runnable objects.

An Handler is associated with only one thread. To send a message to a thread we use an Handler object associated with that thread. This association is defined by which Looper was used to create the Handler.
We will now go through the basic components of this communication framework to learn what each of them does.

The MessageQueue

Every thread that can receive messages owns one MessageQueue. When sending a message using Handler, what we send ends up in the MessageQueue of the thread associated with the Handler. It is important to specify at this point that for every thread we can have as many Handler objects as we want. However there is only one MessageQueue shared by all the Handlers that collects all the messages for a thread. Usually it is not that important to know about the MessageQueue. It is well hidden by the Handler class and we as a developers never access it directly. However it is worth mentioning to provide some background on how messages travel across thread boundaries.

The Looper

Every thread that can receive messages owns a Looper. This object performs the dirty job of looping over the thread’s MessageQueue and delivering the messages to the correct Handler. Remember that a thread can have many Handlers associated with it, therefore it is important that messages sent through an Handler are also received by that same Handler.

In most practical cases the Looper performs all it’s magic behind the scenes. This is the case when we use HandlerThread for instance. However if we want to use a plain old thread we have to set up the looper manually. At this point it is important that we understand the difference between different kind of threads in Android.

  • A worker thread is a thread that starts, performs some operation, and then terminates.
  • A looper thread is a thread that starts and does not terminate unless instructed to do so. It keeps looping over a message queue waiting for events to be processed. The UI thread is an example of a looper thread.

In the following sections we are going to see how to create a looper thread and use it to receive and process messages.

Send messages and runnables between threads using Handler.

We went over all the basic components, these building blocks work together to create the Handler API. Now let’s move on to a practical example. We are going to create a simple looper thread that can process messages and execute runnable objects.

Create the looper thread

The following code creates a looper thread by subclassing Thread.

class MyLooperThread : Thread() {
    override fun run() {
        Looper.prepare()
        Log.i("MyLooperThread", "Looper is looping")
        Looper.loop()
        Log.i("MyLooperThread", "Looper finished looping")
    }
}
...
val thread = MyLooperThread()
thread.start()

When this thread starts it creates a looper with Looper.prepare(). Once the looper is ready the thread simply calls Looper.loop() to start the looper. Notice that when this code runs we get the following output on logcat

MyLooperThread: Looper is looping

What we do not get on logcat is

MyLooperThread: Looper finished looping

This means that the call to Looper.loop() does not return, because as the name suggests it keeps looping on the message queue. If we want to terminate this looper thread we have to do it explicitly by stopping the looper. This allows the call to Looper.loop() to return and so the thread can finish. We will see how to do this in a bit.

Create the Handler

Now let’s see how to create an handler that will deliver messages to this thread. The following code is a bit more complicated, so we split it into three parts and explain them in details. The first part is an evolution of the looper thread we saw above, we added a looper member which contains a reference to this thread’s looper.


class MyLooperThread : Thread() {
    var looper: Looper? = null
    override fun run() {
        Looper.prepare()
        Log.i("MyLooperThread", "Looper is looping")
        looper = Looper.myLooper()
        Looper.loop()
        Log.i("MyLooperThread", "Looper finished looping")
    }
}

With the looper member we will be able to create the handler object. We also need a subclass of Handler that implements the handleMessage() method. This method gets called when a new message is dispatched to the handler, therefore it must contain the logic that describes what happens when a message is received.


class MyHandler(looper:Looper) : Handler(looper) {
    override fun handleMessage(msg: Message) {
         when (msg.what) {
             1 -> Log.i("MyHandler", "Received message 1")
             2 -> Log.i("MyHandler", "Received message 2")
             3 -> Log.i("MyHandler", "Received message 3")
         }
    }
}

Notice that we are using the constructor of Handler that takes a Looper parameter. We will be able to provide the looper of our thread as a parameter when instantiating the handler. There is also another constructor for Handler which does not accept any parameter, when this constructor is used the Handler is created using the looper object of the thread that is creating the handler. This is not what we want in this case because we are creating the handler from the main thread. Therefore we have to use the explicit constructor.

Put it all together

We have the looper thread and the handle run place, let’s use them and send a few messages to the looper thread from the main thread. Assume the following code is running in the main thread, for instance in your activity’s onCreate() method.


// Start the looper thread
val thread = MyLooperThread()
thread.start()

// Wait for the looper thread to prepare the looper
Thread.sleep(1000)

// Create two simple messages
val messageOne = Message()
messageOne.what = 1
val messageTwo = Message()
messageTwo.what = 2

// Send the messages
val handler = MyHandler(thread.looper!!)
handler.sendMessage(messageOne)
handler.sendMessage(messageTwo)

Here we create the handler using the thread’s looper. Notice that there is a Thread.sleep(1000) after the thread is started. This call pauses the current thread for one second (1000 ms) and then resumes the execution. This pause is intended to give the thread enough time to prepare its looper. After the call to sleep() returns we are going to use the looper object to create the handler. Please never use Thread.sleep() to deal with threads synchronisation, it is not the proper way and can be dangerous. I am doing it here just to keep the code simple.

This code produces the following output

MyLooperThread: Looper is looping
MyHandler: Received message 1
MyHandler: Received message 2

Now that we’ve covered the basics let’s move on to some more advanced functionalities of the handler API.

Post runnable objects using Handler

Posting simply means sending a message that contains a runnable object. The runnable object will be executed in the context of the receiving thread.

To post a runnable object all we have to do is call post() providing the runnable. The run() method of the runnable will run inside the looper thread.


val myRunnable = Runnable { 
    Log.i("myRunnable", "This is myRunnable running")
}
      
handler.post(myRunnable)

Running this code will print “This is myRunnable running”.
It is also possible to schedule a runnable object for execution at some point in the future.


val myRunnableDelayed = Runnable {
     Log.i("MyRunnable", "This is myRunnableDelayed running")
}

Log.i("MyApp", "Scheduling delayed runnable now")
handler.postDelayed(myRunnableDelayed, 2000)

We can see from logcat that the runnable prints its message two seconds after “Scheduling delayed runnable now”

18:58:21.742 ... MyApp: Scheduling delayed runnable now
18:58:23.746 ... MyRunnable: This is myRunnableDelayed running

Send messages to the future

A powerful feature of the handler API is the ability to send messages to the future. We can use different versions of sendMessage() and specify when we want our message to be delivered.


handler.sendMessageAtTime(messageOne, 12345678)
handler.sendMessageDelayed(messageTwo, 1000)

The first call sends a message and specifies the absolute time at which it will be delivered. The absolute time is specified as the number of milliseconds since the device powered on. We can always figure out what this number is using SystemClock.uptimeMillis(). The second call,  sendMessageDelayed(), uses a relative time instead. This is simply the number of milliseconds , from the time we are calling the method, after which the message will be delivered.

Remove scheduled messages

When scheduling a message or a runnable it is important to have the ability to unschedule it. There are many reasons for this, imagine for example a button that when clicked starts a countdown for an action, and if the user presses the button again before the time reaches zero then the action is cancelled.


override fun onClick(v: View?) {
    if (!actionScheduled) {
        handler.postDelayed(myRunnableDelayed, 2000)
    } else {
        handler.removeCallbacks(myRunnableDelayed)
    }

    actionScheduled = !actionScheduled
}

We have to provide the runnable object when we want to unschedule it. As a more brutal alternative we can also remove every runnable that was posted on the handler with handler.removeCallbacks(null).
In order to unschedule a message we can use removeMessage() instead


override fun onClick(v: View?) {
    if (!handler.hasMessages(messageOne.what)) {
        handler.sendMessageDelayed(messageOne, 2000)
    } else {
        handler.removeMessages(messageOne.what)
    }
}

Notice the difference with the runnable case. We used handler.hasMessages(), another useful method to check wether or not the message was already scheduled on the handler.

Quit a looper thread

We saw at the beginning that the call to Looper.loop() does not return, unless we explicitly tell the looper to stop processing messages. It is very important to always remember to do this, or the looper thread will just stay there forever, causing a leak. Finding the right way to quit a looper thread can be challenging, because we have to access the looper object and call the quit() method on it.

The following is one possible way. It uses a specific message for quitting the looper. It is easy to do this from inside the handler since it has access to the looper object.


class MyHandler(looper:Looper) : Handler(looper) {
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            1 -> Log.i("MyHandler", "Received message 1")
            2 -> Log.i("MyHandler", "Received message 2")
            3 -> looper.quit()
        }
    }
}
...
val messageOne = Message()
messageOne.what = 1
val messageTwo = Message()
messageTwo.what = 2
val messageThree = Message()
messageThree.what = 3

// Send the messages
val handler = MyHandler(thread.looper!!)
handler.sendMessage(messageOne)
handler.sendMessage(messageTwo)
handler.sendMessage(messageThree)
thread.join()
Log.i("MyApp", "Looper thread finished")

When a message with what set to three is received we quit the looper. Notice that we added a call to thread.join(). This call blocks the current thread, which is the main thread, until thread has finished executing. We just stopped the looper so we know this is going to happen very soon, but we still have to wait in order to be sure.

Extending the code we have with this new piece we get the following output on logcat

MyLooperThread: Looper is looping
MyHandler: Received message 1
MyHandler: Received message 2
MyLooperThread: Looper finished looping
MyApp: Looper thread finished

Conclusion

This post is an introduction to handler and looper in Android. It covers 80% of what there is to know about them. You might have noticed that creating a looper thread like we did is not the easiest thing. There is some synchronisation involved in at least two places, when the looper thread starts and when it stops. We dealt with the problem with some really naive methods for the sake of simplicity. However this is still far from being a good approach. In the next post we will see how to use another powerful class that makes everything a lot easier, the HandlerThread.

Unique opportunity! Help a fellow grow his blog!

Hi there! If you’ve read this far maybe you think this was useful, or fun, or I don’t know what but for some reason You Got Here! Great! Please consider sharing this post with your network, I am trying to get The Code Butchery to grow so I can provide more content like this, will you help me in my journey? Thank you!

Advertisements
Share this

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.