消息机制涉及四大类:
1、Handler:用来发送和处理消息。持有 Looper、MessageQueue 引用。
2、Looper:用来存储消息队列以及处理消息循环。
3、MessageQueue:调度消息列表。链接数据结构。
4、Message:包含描述和任意数据对象的消息。
一、大致流程
创建Handler流程:
发送、处理message流程:
二、如何使用Handler发送消息
1. 创建 Handler
1.1 Handler 构造方法
分为两类:不指定Looper和指定Looper。
1)先来看看不指定Looper的构造方法:
public Handler(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(Callback callback, boolean async) 方法,可以看到 mLooper 是通过Looper.myLooper() 获取,这就意味着当前线程必须关联了Looper,待会看看 Looper.myLooper() 的源码。
2)看看指定Looper的构造方法:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
非常简单,直接使用外面传入Looper。
1.2 Loop.myLooper()
获取当前线程关联的 Looper。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public class ThreadLocal<T> {
...
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
...
}
最终调用了 ThreadLocal 的 get(),在其方法中可以发现 ThreadLocalMap 存储的就是每条线程关联的 Looper。
那么线程是如何关联 Looper,存放在 ThreadLocalMap 中的呢?从Handler无Looper的构造方法抛出异常信息中可以发现是 Looper.prepare()。
1.3 Looper.prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
该方法就是一切的起源,必须调用才能为当前线程关联 Looper。
这时想到平时使用 Handler,直接创建了不指定的 Looper,那是因为主线程内部调用了Looper.prepareMainLooper() 方法。
public final class ActivityThread extends ClientTransactionHandler {
...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...
Looper.loop();
...
}
...
}
小结:每条线程只能有一个Looper、MessageQueue。
2. 发送消息
2.1 handler 发送消息
分为两大类:sendXXXMessage() 系列、postXXX() 系列。
1)先看看 sendXXXMessage() 系列的方法
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
2)看看 postXXX 系列的方法
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
postXXX 系列方法,通过 getPostMessage() 将Runnable 作为Message 的callback 字段的值 ,包装转换成Message。
这两类方法最终调用 sendMessageAtTime() 方法,
public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以发现使用到了MessageQueue,最终调用了MessageQueue的方法将msg塞入队列中。
2.2 MessageQueue 的 enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
参数 when 字段就是 msg 开始执行的时间。按照开始执行时间的顺序从小到大排列,将 msg 插入合适的位置。(单链表数据结构)
这部分只是不断的往queue中添加msg,那么什么哪里回调执行msg呢?
3.执行消息
3.1 Looper.loop()
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;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
死循环不断的从队列中取出消息执行。
queue.next(); 从循环队列中取出下一条msg,当 queue 没有消息时就是阻塞挂起线程(调用 nativePollOnce()),一直等到下一条 msg 插入时才会被唤醒。
msg.target.dispatchMessage(msg); 这里就是调用Handler分发消息,执行消息的地方。 msg.target 是在 Handler 的 enqueueMessage() 赋值。
3.2 handler.dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
分发处理消息,msg.callback 就是Runnable,也是postXXX系列的方法;否则如果创建 handler 时调用了含 Callback 参数的构造方法,那么会先回调 callback 的 handleMessage() ,否则调用 handler 的 handleMessage()。
值得注意的是:如果 callback 的 handleMessage() 返回值为 false,那么也会执行 handler 的 handleMessage()。
总结:以上就是handler消息的整个流程,简单来说就是 Looper 不断循环从MessageQueue 从取出 MSG 分发给对应线程的Handler执行。
三、问题
1、为什么主线程中 Looper 中 loop 里的死循环没有造成卡死(ANR)。
首先要明白卡死是指应用程序无响应,而应用程序无响应的根本原因是在主线程执行了耗时操作。而 loop() 是从循环队列中取出消息进行消费,当 messageQueue.next() 没有消息时,就会调用 nativePollOnce() 阻塞挂起主线程,当有 message 时,native 层会唤醒主线程。阻塞和唤醒机制是 Linux 中的 epoll+pipe 机制。
(题目关键点在于:主线程被阻塞怎么没有造成ANR。)
Activity 响应时间要求:5s。
Service 响应时间要求:20s。
Broadcast 响应时间要求:10s。
2、为什么在子线程使用 Handler 会报错。
因为 Handler 中无参构造方法中有 Looper.myLooper() 用来获取当前线程的 Looper,当前线程的 Looper 则是通过 Looper.prepare() 创建 Looper 对象并塞进 ThreadLocal() 中。而主线程可使用Handler,是因为 ActivityThread 的 main() 主动调用了 Looper.prepareMainLooper()。
3、Handler 如何切换线程?发送消息在子线程,handleMessage() 中处理却是 Handler 的关联线程?
handler.sendMessage() 方法只是在当前线程中执行 message 插入到 MessageQueue 的操作,并不是执行 message的线程。而执行 message 的线程则是 Handler 的初始化线程( Looper 持有的线程),因为 Looper.loop() 才是真正执行 message 的地方。
四、扩展
HandlerThread、Message.obtain()、ThreadLocal、Linux的epoll+pipe机制。