Handler 笔记总结
本次涉及到的源码为 Source for Android 27.
首先,需要明确的就是,Handler 的主要作用就是将一个任务切换到某个指定的线程中去执行。
而 切换到指定的线程
,说白了就是在线程中调用某方法,即可切换到指定线程,例如一个方法 m1()
,在线程 A 中被调用执行,那么就是切换到了 A,在线程 B 中被调用执行,那就是切换到了 B。
对于 Handler
的实现,主要涉及到的有 ThreadLocal
、MessageQueue
、Looper
。
其中,有关于 ThreadLocal
可以参阅:ThreadLocal 实现原理总结
1、MessageQueue
MessageQueue
是一个消息队列(虽然内部是基于单链表的数据结构实现的),用于存储 Handler
发送的 Message
。主要涉及到的操作就是 enqueueMessage(),往消息队列中插入一条消息
与 next(),从消息队列中读取一条消息并从消息队列中移除该消息
。
源码部分的注释参阅自:Android MessageQueue源码分析
该文章会介绍 MessageQueue 消息的插入 (enqueueMessage) 和读取 (next),native 层的消息机制,以及 IdleHandler 和 SyncBarrier 的逻辑原理。
1.1、boolean enqueueMessage(Message msg, long when)
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;// mMessages 指向链表的第一个 Message 节点
boolean needWake;
// 如果队列为空,或者当前处理的时间点为0(when 的数值,when 表示 Message 将要执行的时间点),
// 或者当前 Message 需要处理的时间点先于队列中的首节点,那么就将 Message 放入队列首部
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;//将 msg 插入到首部
mMessages = msg;//将当前 msg 复制给成员变量 mMessages,也就是将首节点赋值给 mMessages
needWake = mBlocked;
} else {
// 否则遍历队列中 Message,找到 when 比当前 Message1 的 when 大的 Message2,
// 将 Message1 插入到该 Message2 之前,如果没找到则将 Message1 插入到队列最后。
// 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) {
//最终还是会退出该 for 循环
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.
// 判断是否需要唤醒,一般是当前队列为空的情况下,next 那边会进入睡眠,需要enqueue这边唤醒 next 函数
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()
总的来说就是将 msg 插入到队列中,涉及到两个参数,第一个好理解,而第二个参数则与使用 handler.sendMessageAtTime(msg,1000);
等时设置的延迟时间有关,在将 msg 插入消息链表的时候,会赋值给 message.when
,会根据设置的 when
的大小来选择插入的节点,总的就是 when
越小越在前。
1.2、Message next()
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
/* 第一步:初始化操作,如果 mPtr 为 null,则直接返回 null,设置 nextPollTimeoutMillis 为 0,进入下一步。 */
final long ptr = mPtr;// mPrt 是 native 层的 MessageQueue 的指针
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//无限循环的
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
/* 第二步:调用 nativePollOnce。
nativePollOnce 有两个参数,第一个为 mPtr 表示 native 层 MessageQueue 的指针,
第二个参数 nextPollTimeoutMillis 表示超时返回时间(-1 表示一直等待,0 立刻返回)。
调用这个 nativePollOnce 会等待 wake,如果超过 nextPollTimeoutMillis 时间,
则不管有没有被唤醒都会返回。*/
nativePollOnce(ptr, nextPollTimeoutMillis); // jni 函数
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
/* 第三步:获取队列的头 Message(msg),如果头 Message 的 target 为 null,
则查找一个异步 Message 来进行下一步处理。
当队列中添加了同步 Barrier 的时候 target 会为 null */
Message prevMsg = null;
Message msg = mMessages;//先赋值为消息链表的首节点
// target 正常情况下都不会为 null,在 postBarrier 会出现 target
// 为 null 的 Message
// 如果设置了 postBarrier() 则就进入到该段逻辑
// 在屏幕刷新机制中,就会触发该段逻辑,因为与屏幕刷新有关的 msg 是异步的
// 且在将异步 msg 添加到主线程的 messageQueue 之前会触发 postBarrier()
// 之后就会通过这段逻辑取出与屏幕刷新有关的异步 msg 并进行处理
if (msg != null && msg.target == null) {// msg.target 即发送该 msg 的 Handler 的引用,一般情况下不为 null
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
// 取到异步消息则会退出循环
}
/* 第四步:判断上一步获取的 msg 是否为 null,为 null 说明当前队列中没有 msg,
设置等待时间 nextPollTimeoutMillis 为 -1。
(当 nextPollTimeoutMillis == -1 就会导致下一次轮询时在
第二步因 jni 函数导致 sleep)
实际上会等待 enqueueMessage 的 nativeWake 来唤醒。
(可参阅 1.1 enqueueMessage() 最后那一部分的注释)
如果非 null,则下一步。 */
if (msg != null) {
/* 第五步:判断 msg 的执行时间 (when) 是否比当前时间 (now) 的大,如果小,则将 msg 从队列中移除,并且返回 msg,结束。
如果大则设置等待时间 nextPollTimeoutMillis为(int) Math.min(msg.when - now, Integer.MAX_VALUE),
执行时间与当前时间的差与 MAX_VALUE 的较小值。执行下一步 */
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
/* 第六部:判断是否 MessageQueue 是否已经取消,如果取消的话则返回 null,否则下一步 */
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
/* 第七步:运行 idle Handler,idle 表示当前有空闲时间的时候执行,而运行到这一步的时候,
表示消息队列处理已经是出于空闲时间了(队列中没有符合条件的 Message,
,因为有 barrier msg 时如果遍历了整个队列也没有找到异步 msg,而此时可能
有同步 msg,或者头部 Message 的执行时间(when)在当前时间之后)。
如果没有 idle,则继续第二步,如果有则执行 idle Handler 的 queueIdle 方法,
我们可以自己添加 Idle Handler 到 MessageQueue 里面(addIdleHandler 方法),执行完后,回到第二步。 */
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next()
方法内部有一个 for(,,)
无限循环的方法,在正常情况下,如果消息队列里面没有消息,则会因为该循环阻塞住(这里会利用底层技术实现 sleep,参阅详细的注释部分,而不是简单粗暴的一直循环,猜测是为了避免性能的浪费),而如果能够正常的取到 message
则会返回该 message
,并将其从消息队列中移除。
2、Looper
Looper
则是扮演消息轮询的角色,内部维护着一个 MessageQueue
实例,它会不停的利用 messageQueue.next()
从消息队列中取 Message
,如果有取到则会立即进行处理,否则就会因为 messageQueue.next()
内部的实现机制而被阻塞在那里。
Handler
在使用的时候,需要在对应的线程里面有实例化的 Looper
,否则就会报错。
new Thread("SubThread") {
@Override
public void run() {
// 在目标线程中创建 Looper 实例(因为是在目标线程中调用的该方法)
// 然后会把该实例保存在 Looper 的静态成员变量 sThreadLocal 中
Looper.prepare();
// 在目标线程中创建 Hanlder 实例,
// 该构造函数内部会把当前线程的 Looper 借助 Looper.sThreadLocal 传递给该 Handler 实例
Handler handler = new Handler();
Looper.loop();
// 需要等 Looper.loop() 执行完后才能执行
Log.d("TAG", "After Looper.loop()");
}
}.start();
2.1、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");
}
// 实例话一个 Looper 实例并通过 sThreadLocal 保存
sThreadLocal.set(new Looper(quitAllowed));
}
2.2、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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//正常情况下,queue.next() 如果不能返回 msg,则会因内部的无限循环以及具体的实现逻辑堵塞在该处
Message msg = queue.next(); // might block
//queue.next() 返回 null 会使得 loop() 方法结束
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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//利用 msg 持有的 target(也就是发送该 msg 的 Handler)对 msg 进行处理
//正常情况下,因为 loop() 方法是在目标线程中被调用
//进一步的这里就会在目标线程中调用
//因此就实现了线程的切换
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
loop()
比较容易理解,其内部也有一个无限循环,目的是为了不断的从消息队列中去取 Message
进行处理。需要注意的是,loop()
内部的无限循环的阻塞是因为执行到
Message msg = queue.next();
时,无法从 queue.next()
内部退出而发生。
如果通过 messageQueue.next()
取到了 message
,则会通过 message
持有的 target
(即发送该 message
的 handler
实例的引用) 调用该 hanlder
实例的 dispatchMessage(msg)
方法,从而实现了线程的切换。
回顾前面说的 切换到指定的线程
的本质,这里因为 loop()
方法是在 SubThread
中调用的,因为 hanlder.dispatchMessage(msg)
也就在 SubThread
线程中被调用。
2.2、Looper 的退出
一个 Looper
实例可以通过 quit() / quitSafely()
退出。如:
// 直接退出
handler.getLooper().quit();
// 设定一个标记,等把消息队列设置标志之前已有的消息处理后才退出
handler.getLooper().quitSafely();
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
可以看到,调用 Looper
的退出方法,实际上就是调用内部的 MessageQueue
的 quit()
来通知消息队列退出,当消息队列被标记为退出状态时,其 next()
方法则会返回 null。
Looper
退出后,调用 Handler
的 send
或 post
方法会返回 false
。
在子线程中,如果手动创建了一个 Looper
,则应在不需要用了之后主动调用退出的方法来终止消息循环,否则这个子线程就会一直处于等待状态,而如果 Looper
退出了,则在线程内部才能正常执行 Looper.loop()
之后的逻辑。
3、Handler
3.1、发送 Message
Handler
可以通过一些列的 send
或者 post
方法来发送 Message
。
对于 post
方法,实际上还是会将传递的 runnable
封装成一个 message
实例,并将 runnable
赋值给 message.callback
。
然后,send
或者 post
方法都会走到:
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);
}
很简单,将 message 插入到内部维护的 messageQueue 中。
3.2、处理 Message
在 2.2 中有说,Looper.loop()
方法内部会将从 messageQueue
中取到的 message
交由对应的 handler
进行处理。
msg.target.dispatchMessage(msg);
而 Hanlder
的 dispatchMessage()
也很简单。
public void dispatchMessage(Message msg) {
// 通过 handler.post() 传递的 callback
if (msg.callback != null) {
handleCallback(msg);
} else {
// handler 实例内部的 mCallback 成员变量
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
// 如果 mCallback.handleMessage() 返回 true 则 return
return;
}
}
// 最后才轮到重写的 handleMessage()
handleMessage(msg);
}
}
public interface Callback {
public boolean handleMessage(Message msg);
}
Callback 的意义在于可以用创建一个 Handler 的实例但不需要派生 Handler 的子类。
下面就是派生子类的实现实例。
class SunHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
3.3、为 Handler 的构造函数
1、通过 Handler(looper)
可以在实例化时指定 Looper
,
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2、使用默认构造函数 Handler()
时如果目标线程没有 Looper
会出错,而平时在 UI 线程中直接实例化时正常,是因为 UI 线程早就有来一个 MainLooper
。
public Handler() {
this(null, false);
}
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;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
根据前面的你内容,可以了解到,一个线程之后对应唯一的一个 Looper,从而对应唯一的一个消息队列,而对于一个线程来说,可以对应多个 Handler
,这数个 Handler
共用唯一的消息队列。
部分内容参阅自《Android 开发艺术探索》