本文讲解 Handler 从子线程发送消息(sendMessage)到主线程接收消息(handleMessage)到底都干了些什么?
Handler 通信五大类:
Handler、Message、Looper、MessageQueue、Thread。
这五大类是怎么工作的?
可以把 Handler 的工作流程想象成一个生产线上的传送带,如下图:
首先是发送消息,发送消息可以理解为工人将产品放到传送带的过程。
Handler 发送消息可以是 send 也可以是 post。
如:sendMessage、sendEmptyMessage、sendEmptyMessageDelayed、postDelayed 等。
以 sendEmptyMessage 举例,查看源码是如何发送消息的。
1.Handler 的 sendEmptyMessage 会调用 Handler 的 sendEmptyMessageDelayed。
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
2.Handler 的 sendEmptyMessageDelayed 会调用 Handler 的 sendMessageDelayed。
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
...
return sendMessageDelayed(msg, delayMillis);
}
3.Handler 的 sendMessageDelayed 会调用 Handler 的 sendMessageAtTime。
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
...
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
4.Handler 的 sendMessageAtTime 会调用 Handler 的 enqueueMessage。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
...
return enqueueMessage(queue, msg, uptimeMillis);
}
5.Handler 的 enqueueMessage 会调用 MessageQueue 的 enqueueMessage。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
...
return queue.enqueueMessage(msg, uptimeMillis);
}
所以,所有的消息,不管调用的是哪个 send 或 post,最终都是将消息发送到消息队列里。
Q:MessageQueue 的 enqueueMessage 干了什么事情?
A:MessageQueue 的 enqueueMessage 就是存储了 Message ,也就是把消息放到消息队列里,而 MessageQueue 的 enqueueMessage 存储消息的方式是一个优先级队列(根据时间进行排序的队列)。
(优先级队列是用链表结构构成的,链表是数据的保存方式,只不过链表里面加入了队列算法(先进先出),而在先进先出的算法基础之上又加入了根据时间进行排序,就变成了优先级队列。)
通过源码看具体逻辑实现:
boolean enqueueMessage(Message msg, long when) {
...
synchronized(this) {
...
// 根据时间进行排序(你添加的 when ,和消息队列的时间节点进行排序)
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
...
// 排序完了之后,找到插入的点,把这个消息放进去
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
...
}
return true;
}
可以看到所有的消息都是根据时间进行排序,这样就形成了一个消息队列,队头的消息最早执行,队尾的消息最晚执行。也叫优先级排序。
Q:这个时间怎么理解?
A:以 Handler 的 sendMessage 举例。
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
如果没有设置时间,那么就是当前时间,意味着立刻执行。那么给 sendMessageDelayed 传入的参数就是 0。
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
那么时间就是 SystemClock.uptimeMillis(系统时间)+ delayMillis 时间。所以每一个消息都会有一个执行时刻。
那么这样我们就可以知道消息一旦发送出去之后就会存在于我的 MessageQueue 里面,那么有消息进来了自然就会有消息出去。
Q:这个消息怎么出去?
A:还是拿传送带举例,工人(sendMessage )把产品(Messgae) 放到了传送带上 ,传送带可以理解为 MessageQueue ,传送带滚动需要提供动力,Looper 就是给它提供动力的函数。提供动力的开关是 Looper.loop() ,或者说动力通上电是通过线程(Thread)调用线程所对应的 Looper 的函数。
Looper.loop() 源码
public static void loop() {
...
// 死循环
for (;;) {
// 从消息队列里取消息
Message msg = queue.next();
if (msg == null) {
return;
}
...
try {
msg.target.dispatchMessage(msg);
...
} catch (Exception exception) {
...
} finally {
...
}
...
}
}
MessageQueue.next() 源码
Message next() {
...
for (;;) {
...
synchronized (this) {
...
if (msg != null) {
if (now < msg.when) {
...
} else {
...
return msg;
}
} else {
...
}
...
}
...
}
}
next 返回值是一个 msg。next 函数就是从这个优先级队列(传送带)里取消息。也就是通上电之后从队头取消息。
Handler.dispatchMessage() 源码
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
...
} else {
...
handleMessage(msg);
}
}
工作流程:传送带通电(Looper.loop),传送带滚轮滚动(MessageQueue.next),滚动之后后就会从消息队列里取消息(Handler.dispatchMessage)。
Q:那么怎样让它停下来呢?
A:查看 Looper.loop() 源码
public static void loop() {
...
// 死循环
for (;;) {
// 从消息队列里取消息
Message msg = queue.next();
...
}
}
如果消息队列为空,那么 queue.next() 就会被 block 住。那么 Message msg = queue.next() 就会有一个睡眠,外层的 for 死循环就会停下来。
如果本文对你有帮助,请点赞支持!!!