读完 Handler 相关的源码,了解了 Handler 在多个线程间进行消息分发的大概流程,以及和 MessageQueue,Looper 之间的关系,但是心中一直还存在一点疑惑:假设在主线程定义的 Handler,在子线程中调用发送消息,是怎么让主线程收到这个消息的呢?粗看好像是 Handler 进行了一次线程切换,但是仔细查看源码便可以发现,Handler 似乎并没有这种能力。
首先我们从子线程中发送消息开始理一下代码大概是怎么走的。首先看 Handler 的初始化代码:
public Handler(@Nullable Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从上面代码可以知道,Handler 初始化的时候,会定义一个 Looper 以及 MessageQueue 类型的成员变量,而且 Looper 为空的话就会报异常,因此在 Handler 初始化前必须先进行 Looper 的初始化。这就是为什么如果在子线程中使用 Handler 必须有如下类似代码:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler =new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
Looper.loop();
}
}).start();
prepare()
方法中是将新建一个 Looper 对象,并将其放进该线程的 ThreadLocal 对象中。Looper 的初始化代码中有会新建一个 MessageQueue 对象:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这样 Handler、MessageQueue、Looper 三者的关系就明了了,我们开始进入正题,当子线程中调用 Handler 发送一个消息时,具体代码参考如下:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
handler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
经过一系列类似方法跳转,最后会进入如下方法中:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
上面代码 Handler 中要传递的消息最后被成员变量 MessageQueue 插入了自身队列中(由单例表实现,方便插入和删除),而后会通过nativeWake()
native 方法唤起此时正在等待 MessageQueue 消息的 Looper。
public static void loop() {
final Looper me = myLooper();
......
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
......
try {
msg.target.dispatchMessage(msg); //here
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
主要看msg.target.dispatchMessage(msg);
这一行代码,target 是在当初调用enqueueMessage()
方法时将自身传递到 Message 中的,最后还是通过调用 Handler 的 dispatchMessage 来处理消息。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this; //here
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
为什么不直接在子线程中调用 Handler 的 dispatchMessage()
,而要绕这么一大圈呢?
我们用代码做一下验证:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
Log.d(MainActivity.class.getSimpleName(), "current thread-->" + Thread.currentThread().getName());
Log.d(MainActivity.class.getSimpleName(), "handleMessage: " + message.what);
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
handler.sendEmptyMessage(1);
Thread.sleep(2000);
Message message = Message.obtain();
message.what = 2;
handler.dispatchMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
输出结果为:
D/MainActivity: current thread-->main
D/MainActivity: handleMessage: 1
D/MainActivity: current thread-->Thread-2
D/MainActivity: handleMessage: 2
可以看到直接调用dispatchMessage()
的话代码还是在子线程中运行,因此可以知道,消息在哪个线程接收,并不是由 Handler 来决定的,而是由初始化的时候 Handler 所绑定的 Looper 对象所在的线程来决定。Looper 对象是在某个线程的 ThreadLocal 中创建的,而 MessageQueue 对象是在 Looper 中创建的,就像一开始 Handler 在子线程中调用一样,后面的 Handler 是在 Looper 所在线程进行dispatchMessage()
方法的调用的,因此那时候该方法已经转到该线程中运行了。
再看一个HandlerThread
使用的例子:
HandlerThread thread = new HandlerThread("test");
thread.start();
Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
Log.d(MainActivity.class.getSimpleName(), "current thread-->" + Thread.currentThread().getName());
}
});
可以看到输出结果为:
D/MainActivity: current thread-->test
使用HandlerThread
有一点需要注意的是线程操作处理完成后要记得主动调用thread.quit();
进行释放,不然其中的 Looper 一直阻塞,线程无法结束。