Handler详解
这篇文章缘起于一道面试题:
Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系
虽然能够大致说明白,但是自己对答案也不太满意,翻一翻源码,从源码角度剖析一番。
1. 概述Handler相关对象模型关系
-
首先看一下Handler、Looper、MessageQueue、Message的相关类图
Handler中有两个成员变量:mLooper、mQueue,分别对应的是Looper和MessageQueue的实例。
Looper中包含一个成员变量mQueue,对应的是MessageQueue的实例。
在创建Handler的时候会获取当前线程的Looper,并将Looper对应的消息队列也赋值给Handler中的mQueue,用来存储消息。 -
Handler的构造方法
平时我们都是new Handler()创建,最后还是调用的两个参数的构造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}-
在构造方法中通过
mLooper = Looper.myLooper();
创建了Looper。- myLooper方法中是从sThreadLocal中获取的Looper:sThreadLocal.get()
-
Looper是在什么地方创建的呢?答案是prepare中,所以在子线程中,没有调用Looper.prepare()不能创建Handler,而主线程默认执行了Looper.prepare(),所以不会有这个问题。
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));
} - sThreadLocal是跟线程相关的,所以,每个线程可以对应一个Looper及MessageQueue
通过Looper,获取到了Looper对应的MessageQueue,并赋值给Handler中的MessageQueue
这样就把Handler和Looper、MessageQueue串联了起来。
-
2. 从源码角度剖析3个流程
-
发送消息
发送消息有两种方式:
sendMessage(Message msg)
,或者post(Runnable r)
,最终都是调用到了sendMessageAtTime(Message msg, long uptimeMillis)
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);
}其中最关键的是
queue.enqueueMessage(msg, uptimeMillis)
消息队列把Message放到了消息队列中,存储起来。 -
分发消息
Looper调用了loop()方法之后,就会不停的从MessageQueue中读取消息,只要读取到消息,就会进行分发处理。下面我们把loop()中读取消息,分发处理逻辑摘出来。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}其中:
Message msg = queue.next();
从消息队列中取出消息
如果消息不为空,则使用Handler的dispatchMessage方法进行分发处理。public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}这里主要有两个分支:
-
handleCallback(msg)
是处理的Runnable任务 -
handleMessage(msg)
是处理的普通消息任务
-
-
处理消息
处理消息就是执行HandleMessage中的逻辑或者Runnable中的逻辑。
-
在看Looper.loop()方法逻辑的时候,最后一行被我删掉了,这一行是等消息分发处理完后对msg的回收处理。
msg.recycleUnchecked();
-
源码如下:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
-
3. 文章开头的面试题答案整理
工作流程:
- 在单线程中,Looper轮询器被调用prepare()和loop()后,它会不断的从MessageQueue头部读取Message;
- 创建Handler时,Handler内部会持有当前线程对应的Looper和MessageQueue的引用。
- 如果该线程中有Handler发送消息给MessageQueue,Looper就能够取出该消息,通过Message的target(Handler)的dispathMessage,进行处理(两个分支,一个处理Runnable,一个处理普通Message)。
- 处理完成后,Looper又继续进行消息的拉取,如此循环往复。直到调用
removeCallbacksAndMessages
可以将当前Handler中的所有任务给取消掉。