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();
}
}
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!
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With