How Looper, MessageQueue, Handler work in Android

Pi Vinci
3 min readOct 27, 2018

I. Asynchronize task

Fetching data from IO and then display it on the screen is a very very common task in creating software. In the Android world, there are many ways we can use to archive this task. We can use RxJava, Asyntask, etc. But, have you ever consider how these things work under the hood, how background thread in Android can make a change in the view which is only updated in the main thread?

The answer is Android using its component mainly include Looper, MessageQueue, Handler, Runnable to manage workload in each Thread. To make an overview of how they work, we can take a look at the below picture

As you can see in this picture, ThreadB contain a Looper, and the Looper manages MessageQueue. MessageQueue contains a list of Message (Runnable). Handler is tight to the MessageQueue and it can push a message to the MessageQueue. Handler also has the responsibility for dequeuing/handling the message from MessageQueue.

II. Looper

Each Looper is associated with one Thread. MainThread by default will have a Looper to handle a MessageQueue of tasks which run in MainThread. However, background Thread by default doesn’t have Looper. If we want to init a Looper for a Thread we need to add Looper.prepare() and Looper.loop() in the run method of Thread. Like the bellow code.

Looper.prepare() to initialize current thread as a looper and to make sure that there is no Looper associated with this Thread before. If there is a Looper is associated with this thread a RuntimeException will be thrown.

Looper.loop() start to make the thread running infinity with a MessageQueue. If you call this function without Looper.prepare(), a RuntimeException also will be thrown.

If we look at Looper.loop() the implementation will simple look like

All instance of Looper will be stored in ThreadLocal and if you want to get the Looper which associated with the current thread we can use Looper.myLooper(), this function will return the looper which associated with the calling thread.

Looper.quit() will immediately terminate the Looper and discard all of the messages inside the MessageQueue. To make sure that all message in the MessaeQueue will be dispatch before quitting, we can use Looper.quiteSafely()

III. Handler

Handler is a component that tight to Looper. Its main responsibility is

  • Creating, Inserting, Removing message from MessageQueue
  • Processing Message in consumer Thread

Each Handler only associated with one Looper and MessageQueue. However, we can associate one Looper with multiple Handlers, and Looper will deliver to the right Handler by sending message to message.target. Because of it, we can say that multiple Handlers associated with same thread will share the same Looper and same MessageQueue.

handler.post(Runnable { 
// Run on thread which associated with this Thread
})

To update a UI stuff we can using Handler with the Main Looper.

Handler(Looper.getMainLooper()).post(Runnable { 
// update ui
})

Inside Android Mainthread, there is some Handler which is necessary for working with View and Input.

  • Choreographer.FrameHandler: handles vsyn and update
  • The ViewRoot.ViewRootHandler: handles input, window event and configuration change.
  • InputMethodManager$H: handles keyboard, touch event.

IV. MessageQueue

MessageQueue contains a list of the message which is sent by Handler. MessageQueue also dispatches messages to Handler.

The order in the Message list is base on the timestamp. The message which has the lowest timestamp will be dispatched first. When the message’s timestamp pass the current time, it will be dispatch.

To get the list of MessageQueue in the current thread we can call the function Looper.myQueue()

V. Conclusion

By understanding how Thread, Looper, Handler, MessageQueue work, we can understand how Asyntask, RxJava, Service work with MainThread. And moreover, with the understanding of them, we can build effective and robust, simple application.

--

--