Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How the Looper knows to send the message to Handler?

The question is, where I tell my Thread to use mHandler for the Looper?

Thank you. I am using the below code:

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();
    }
}
like image 776
Aminadav Glickshtein Avatar asked Dec 25 '12 12:12

Aminadav Glickshtein


2 Answers

The question is, where I tell my Thread to use mHandler for the Looper?

You don't need to tell it explicitly, because the system (framework) does it for you. When you instantiate the Handler, it will automatically obtain access to the message queue of your current Thread. Quoting your comment:

How the system know to send the message to the mHandler Handler?

I'll detail it below.

This is the constructor of android.os.Handler in Android:

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;

As you can see, first it obtains the Looper of your current Thread. The source code of Looper.myLooper() is as follows:

public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

It obtains it from the thread local storage. Later, when you send a Message with this Handler, the Handler actually sets itself as the recipient of the Message: this is how the Looper will know where to dispatch the Message when it arrives. In details:

When you call mHandler.sendMessage(), eventually this code runs (among many other code lines):

    MessageQueue queue = mQueue;
    boolean sent = false;
    if (queue != null) {
        msg.target = this; // msg is your Message instance
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }

As you can see, it sets the Handler instance as the target of the Message. So, later, when the Message is dispatched, it will contain the Handler as its target. This is how the Looper will know which Handler it should dispatch it to. In details, when you call Looper.loop(), the following happens for each of your Message instances in the queue:

msg.target.dispatchMessage(msg);

The dispatchMessage() code is the following:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Notice the last handleMessage(msg) call -- this is exactly your handleMessage(msg) override!

like image 182
Thomas Calc Avatar answered Nov 04 '22 14:11

Thomas Calc


To have a better understanding, create a normal Thread and try creating a Handler in the run() method of that thread. You'll get a RuntimeException saying:

Can't create handler inside thread that has not called Looper.prepare()

Now calling Looper.prepare() in the run() method before creating a Handler would create a new Looper object associated with the calling thread. The source of your confusion is that Looper.prepare() does not take a Thread as argument. It need not, since it's a static method, which internally gets the ThreadLocal of the currently running thread. There can be at most one Looper associated with any Thread.

Now, calling new Handler() would associate the new Handler object with the Looper of the current Thread by internally calling Looper.myLooper(). You can create more than one Handler each with its own Callback in the same Thread. All Handlers would get their messages from the message queue of the same Looper.

like image 30
Dheeraj Vepakomma Avatar answered Nov 04 '22 14:11

Dheeraj Vepakomma