Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Today I read some blogs and source code about how Handler & Looper work together.

Based on what I've learnt, we can have only one Looper on each thread by using the ThreadLocal magic. Usually Handler is initiated in main thread, or else you must manually start or saying, prepare the Looper on a separate thread and then loop it up.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

What really confused me was the loop() in main thread. As I read this in the source code of Looper. It's an endless loop to handle the message queue and then dispatch messages for callbacks to handle.

According to this https://stackoverflow.com/a/5193981/2290191, Handler and it's Looper run in the same thread.

If there is an endless loop on the main thread, wouldn't it block the entire UI system?

I know that I must be so silly to miss something. But it would be lovely for someone to reveal the secret behind this.

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}
like image 804
Ryan Hoo Avatar asked Mar 11 '16 03:03

Ryan Hoo


People also ask

Does handler run on the main thread?

Android handles all the UI operations and input events from one single thread which is known as called the Main or UI thread. Android collects all events in this thread in a queue and processes this queue with an instance of the Looper class.

What is the relationship between Looper handler and MessageQueue in Android?

Looper drains the MessageQueue. Handler is created for some particular Looper and is used to post messages to it and handle these messages when they are processed. The relations are the following: Each Looper has exactly 1 MessageQueue.

What kind of method prepare () is of Looper class?

Creating Looper and MessageQueue for a Thread: prepare() identifies the calling thread, creates a Looper and MessageQueue object and associate the thread with them in ThreadLocal storage class. Looper. loop() must be called to start the associated looper.

How does Looper work android?

Looper is a class which is used to execute the Messages(Runnables) in a queue. Normal threads have no such queue, e.g. simple thread does not have any queue. It executes once and after method execution finishes, the thread will not run another Message(Runnable).


2 Answers

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.

like image 137
Gabe Sechan Avatar answered Sep 24 '22 03:09

Gabe Sechan


This question is a delicate trap. Why infinite loops do not block UI threads because all UI threads behavior are starting from msg.next.

If there is no message, it means that no updates are required. All of our code is just a callback, such as Application onCreate, Activit onCreate, BroadcastReceiver onReceive.

All update callbacks are caused by the message, and these messages are from the system services, such as ActivityManagerService, InputManagerService, WindowMangerService. If you need to update the UI, the android service will send a message to the loop via the IPC.

So the infinite loop is infinite update.

like image 24
Snow Albert Avatar answered Sep 25 '22 03:09

Snow Albert