Handlers, Messagequeue, Looper, Do They All Run on the UI Thread

Handlers, MessageQueue, Looper, do they all run on the UI thread?

Short answer: they all run on the same thread. If instantiated from an Activity lifecycle callback, they all run on the main UI thread.

Long answer:

A thread may have a Looper, which contains a MessageQueue. In order to use this facility, you would have to create a Looper on the current thread by calling (the static) Looper.prepare(), and then start the loop by calling (the also static) Looper.loop(). These are static because there is only supposed to be one Looper per thread.

The call to loop() usually does not return for some time, but keeps taking messages ("tasks", "commands" or whatever you like to call them) out of the MessageQueue and handles them individually (e.g. by calling back a Runnable contained in the message). When there are no messages left in the queue, the thread blocks until there are new messages. To stop a Looper, you have to call quit() on it (which probably does not stop the loop immediately, but rather sets a private flag that is checked periodically from the loop, signaling the it to stop).

However, you cannot add messages to the queue directly. Instead, you register a MessageQueue.IdleHandler to wait for a queueIdle() callback, in which you can decide if you wish to to something or not. All handlers are called in turn. (So the "queue" isn't really a queue, but instead a collection of callbacks to be called regularly.)

Note regarding the previous paragraph: This I actually guessed. I couldn't find any documentation on that, but it would make sense.

update: see ahcox' comment and his answer.

Because this is a lot of work, the framework provides the Handler class to simplify things. When you create a Handler instance, it is (by default) bound to the Looper already attached to the current thread. (The Handler knows what Looper to attach to because we called prepare() earlier, which probably stored a reference to the Looper in a ThreadLocal.)

With a Handler, you can just call post() to "put a message into the thread's message queue" (so to speak). The Handler will take care of all the IdleHandler callback stuff and make sure your posted Runnable is executed. (It might also check if the time is right already, if you posted with a delay.)

Just to be clear: the only way to actually make a looping thread do something is to post a message to it's loop. This is valid until you call quit() on the looper.


Regarding the android UI thread: At some point (probably before any activities and the like are created) the framework has set up a Looper (containing a MessageQueue) and started it. From this point on, everything that happens on the UI thread is through that loop. This includes activity lifecycle management and so on. All callbacks you override (onCreate(), onDestroy()...) are at least indirecty dispatched from that loop. You can see that for example in the stack trace of an exception. (You can try it, just write int a = 1 / 0; somewhere in onCreate()...)


I hope this makes sense. Sorry for being unclear previously.

What is the relationship between Looper, Handler and MessageQueue in Android?

A Looper is a message handling loop: it reads and processes items from a MessageQueue. The Looper class is usually used in conjunction with a HandlerThread (a subclass of Thread).

A Handler is a utility class that facilitates interacting with a Looper—mainly by posting messages and Runnable objects to the thread's MessageQueue. When a Handler is created, it is bound to a specific Looper (and associated thread and message queue).

In typical usage, you create and start a HandlerThread, then create a Handler object (or objects) by which other threads can interact with the HandlerThread instance. The Handler must be created while running on the HandlerThread, although once created there is no restriction on what threads can use the Handler's scheduling methods (post(Runnable), etc.)

The main thread (a.k.a. UI thread) in an Android application is set up as a handler thread before your application instance is created.

Aside from the class docs, there's a nice discussion of all of this here.

P.S. All the classes mentioned above are in the package android.os.

Is a Handler a Thread or not, and what is the role of a Looper with Handlers and Threads?

premise : A Handler is not a Thread.

A Looper is an Object associated with the Thread from which it is created. As you can guess by it's name a Looper is going to loop over something, but looping over what ? Over a message queue also associated with the same thread.

Next question is: How can I put something in this message queue ?

And here is the Handler. A Handler is always associated with a Looper (which one ? we will see it later). The Handler can play 2 roles (and that's maybe why it is confusing)

First role of the Handler : you must use it to post messages to it's associated Looper (in fact to it's message queue). You can use one of the various Handler.sendMessage* (or Handler.post*) methods to do that. (and note the sendMessageDelayed/postDelayed methods allowing you to post a Message/Runnable to be handled in future)

What is the Looper associated with a Handler ? Very easy : the Looper of the current Thread if you don't specify it; but you can use the constructor with a Looper : new Handler(Looper looper) and in this case the handler is associated with looper in argument.

At this point, we know that :

  • a Looper is associated with one and only one Thread
  • a Looper loops over it's associated message queue
  • as a consequence : there is one message queue associated with one Thread (as soon as we have a Looper for the Thread)
  • a Handler is always associated with one Looper
  • a Handler can be used to post message to the message queue

Now, let's see the second part : the message processing/message handling.

First, let's look at the Looper looping over it's message queue.

Is there is a message in the queue ? Yes (i.e. at some point, a Handler has posted it.)
Is it time to handle this message (if it was posted with postDelayed) ? If not, wait a little. If it is time : let's dispatch this message.

Remember that I told that the Handler have 2 roles... and here is the second role of the Handler : a Handler (as indicated by it's name) can handle messages. To be able to handle custom messages you must subclass the Handler class and implements the handleMessage(Message) method.

So, the Looper will simply call the handleMessage of the Handler who posted the message and it's job (i.e. dispatching the messages) is finished (the Looper can move on to the next Message in the queue).

At this point you may ask yourself : "OK I see the interest of delayed messages, but why should I use all this stuff for things to do immediatelly ?"

Remember that the Looper is associated with one Thread and the handleMessage will be called in this Thread. On the other hand, the Handler.post* can be called from another thread. So this mechanism is also very convenient to schedule a job in thread X from thread Y. (particularly useful if the job is affecting the UI and MUST be run in the UI-Thread)

Final note

  • UI-thread is a first class citizen :

On Android, there is a main Looper associated with the main Thread (i.e. the UI-thread). You can get a reference to it with Looper.getMainLooper(), so you create a Handler associated with the main Looper with :

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

and with that you can post a message from any thread to the UI-thread

  • Should you really use messages and subclassing Handler to use this ? No (not always)

You don't always need to create message explicitly to use this mechanism. You can easily post a Runnable to a Handler and in this case you don't even need to override the handleMessage(Message) because the default implementation of the Handler will simply execute the Runnable (under the hood : a message is created with the Runnable associated to it)

  • Looper must be prepared (to receive messages)

By default there is no Looper on every thread (by default, there is only a prepared one in the UI-Thread). To prepare a Looper for the current thread : call Looper.prepare()

What is the difference between these methods for running code on UI Thread?

In Android, a Thread might have one Looper or MessageQueue. Handler is used to send Message or post Runnable to MessageQueue of a Thread, and it must always be associated with a Looper or a MessageQueue of a Thread.

Method 1

new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

When open an app, Android create a new thread (called main thread or UI thread) with a Looper and MessageQueue, this thread is used to render UI and process input events from users.

The above code is create a Handler and associated with Looper of UI thread, so the runnable will be queued to MessageQueue of UI thread, and will be executed later.

Method 2

new Handler().post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

Creating a Handler and associated with Looper of current thread, there are 3 cases:

  • If this code is executed on UI thread, then the runnable will be queued to MessageQueue of UI thread and will be executed later.
  • If this code is executed on a background thread, if this thread has a Looper, then the runnable will be queued to MessageQueue of background thread and will be executed later.
  • If this code is executed on a background thread and this thread has no Looper, then an exception will be thrown.

Method 3

runOnUiThread(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

runOnUiThread is just a utility method of Activity, it used when you want to execute some code on UI thread. The logic behind this method is if current thread is UI thread, then execute it immediately, otherwise used Handler to post a message to MessageQueue of UI thread (like method 1).

Why main thread's Looper.loop() doesn't block UI thread?

Actually the Looper in the main thread is what allows drawing. When a view is invalidated, a message is passed to the main Looper telling it that a draw was requested. When the Looper processes that message, the actual drawing occurs. The reason other activity that holds up the UI thread holds up drawing is that it prevents the Looper from processing that draw message.

This is more or less how drawing works in any event based system, from Windows to Mac to Android.

Why not draw immediately instead of sending a message? Performance. Drawing is slow. If you do multiple changes in response to an event, you don't want to redraw the screen for each one. Doing it this way means you bunch all of your redraws for handling a single event into 1 redraw. For example if you set the text of 1 view and the image of another, they'll both be redrawn at the same time, and only once.



Related Topics



Leave a reply



Submit