前言
一句话总结 Handler
,它是Android消息机制中一个辅助工具,用于子线程与主线程进行通信。
本文的源码将基于Android10,SdkVersion-29
一、Handler具体能做什么
1.1 看下源码的注释
- A Handler allows you to send and process {@link Message} and Runnable
- objects associated with a thread's {@link MessageQueue}.
Handler让你可以发送和处理与某个线程的MessageQueue(消息队列)相关联的Message(消息)和Runnable对象
- Each Handler instance is associated with a single thread and that thread's message queue.
每一个Handler实例都关联一个线程和这个线程的MessageQueue(消息队列)
- When you create a new Handler, it is bound to the thread message queue of the thread that is creating it
- -- from that point on, it will deliver messages and runnables to that message queue and execute
- them as they come out of the message queue.
当你创建了一个新的Handler时,它就绑定到创建它的线程的MessageQueue(消息队列),从那时起,Handler将Message和Runnable对象传递到消息队列,并在它们从队列中返回的时候处理
-
There are two main uses for a Handler:
-
(1) to schedule messages and runnables to be executed at some point in the future;
安排Message和Runnable在将来某个时间执行
-
(2) to enqueue an action to be performed on a different thread than your own.
-
在不同的线程中执行操作
1.2 Handler原理解析
源码的注释中写的很明白 Handler
是绑定了创建它的线程以后进行处理Messager和Runnable对象的。
首先看Handler构造函数
在Handler构造函数中,可以看到上面注释提到的,创建一个新Handler实例都会关联一个线程和这个线程持有的MessageQueue
,通过 Looper.myLooper()
获取当前线程的Looper,拿到Looper之后将Handler中的mQueue赋值
接着写个调用Handler发消息的例子
开启一个新线程调用Handler发送消息的函数,以 sendMessage(Message msg)
函数为例,追踪一下这个函数里都做了什么(除了图中的函数,post(Runnable r)
也会通过getPostMessage(Runnable r)
把Runnable对象添加到Message对象后调用send函数)。
调用顺序:sendMessage()->sendMessageDelayed()->sendMessageAtTime()->enqueueMessage() 这里重点看一下后面2个函数都做了什么
sendMessageAtTime()
这个函数中拿到了实例Handler时的MessageQueue对象,上文提到过的mQueue
,然后调用enqueueMessage()
函数。在这个函数中首先给msg的target变量赋值为当前的Handler对象,接着调用MessageQueue的enqueueMessag()将消息插入到消息队列。
二、MessageQueue
2.1 消息入队
部分源码
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 {
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.
//调用nativeWake,以触发nativePollOnce函数结束等待
if (needWake) {
nativeWake(mPtr);
}
}
}
消息入列就是根据Message的when属性的大小进行排序,先执行的放在队列的前面或者遍历消息队列,把当前进入的消息放入合适的位置。
2.2 消息出队
部分源码
```
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.
//如果退出消息消息循环,那么就直接退出
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//执行native层消息机制层,
//nextPollTimeoutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。
//如果值为0,则无需等待立即返回。该函数可能会阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
//获取系统开机到现在的时间,避免误差,
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//判断是否有消息屏障,同时获取消息队列下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
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;
}
// 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();
}
//如果空闲的Ddel handler个数为0,继续让线程阻塞
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);
}
// 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.
将空闲处理程序计数重置为0,不再运行。
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.
唤醒Native消息机制层
nextPollTimeoutMillis = 0;
}
}
到这里可以看到 MessageQueue
是消息的管理者,提供了入队和出队的方法,具体的调用是在 Looper
进行的。
ps:说实话这部分源码看的懵懵的,这块需要去深入了解一下Native消息机制
三、Looper
3.1 Looper是如何创建的
之前看Handler构造函数的时候,可以发现在实例化的时候,会判断当前线程mLooper
是否存在,也就是说创建一个Handler时也要创建一个Looper。
mLooper = Looper.myLooper();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
通过这个函数获得当前线程的Looper,在主线程中系统已经帮我们创建好了。
ActivityThread
就是Android主线程或UI线程,在ActivityThread的 main()
函数中Looper会通过Looper.prepareMainLooper()
函数创建sMainLooper
子线程中创建Handler
3.2 Looper如何与主线程关联的
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));//创建Looper对象,放入ThreadLocal中。这里的boolean参数详见3.3
}
ThreadLocal的作用
源码注释
- This class provides thread-local variables. These variables differ from
- their normal counterparts in that each thread that accesses one (via its
- {@code get} or {@code set} method) has its own, independently initialized
- copy of the variable. {@code ThreadLocal} instances are typically private
- static fields in classes that wish to associate state with a thread (e.g.,
- a user ID or Transaction ID).
ThreadLocal提供线程局部变量。这些变量与正常变量不同,每一个线程访问自身的局部变量时,有它自己的,独立初始化的变量副本。ThreadLocal变量通常是与线程关联的私有静态字段(例如user ID或Transaction ID)。
回过头来看一下sThreadLocal.set(new Looper(quitAllowed))set(T valye)
函数
总结一下,Looper是通过ThreadLocal来关联主线程的,并且通过ThreadLocal储存保证其线程的私有性。其实不光是主线程,每个线程的Looper都是这样关联的。
3.3 Looper和MessageQueue怎么关联的,循环可以退出吗?
Looper既然可以开始构建消息循环,那么必然提供了退出的函数。
首先看下Looper的构造函数
quitAllowed
,是否允许退出。这时需要去看下这个boolean变量在MessageQueue是怎么用到的
这里发现退出循环的方法也是MessageQueue给Looper提供的,在Looper中调用public void quit() {
mQueue.quit(false);
}
中止循环。注意: 3.2提到的boolean参数,在主线程中创建Looper时,
prepare(false)
传入的是flase,主线程是不允许Looper退出的。原因也很好理解,退出就意味着App挂了。子线程创建Looper默认传入是true
3.4 Looper的消息循环
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
public static void loop() {
final Looper me = myLooper();//获取当前线程的Looper
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 调用上文提到的MessageQueue提供的next()方法,
此方法可能会阻塞
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
省略部分代码
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
//调用Handler的dispatchMessage函数分发消息(Message对象)
msg.target.dispatchMessage(msg);
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.recycleUnchecked();
}
}
总结一下就是无条件循环获取MessageQueue中的消息,然后通过msg.target(为Handler对象)调用
dispatchMessage函数进行消息的分发。最终又回到了Handler处理消息
Handler.dispatchMessage(msg)
这里引用一张图流程图吧
最开始写了个例子发送了消息,现在需要重写handerMessage函数处理消息Handler Looper Message MessageQueue 关系图解
Handler通过sendMessage()发送Message到MessageQueue队列; Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理; 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作。四.相关知识点总结
1.Handler持有MessageQueue对象的引用,通过enqueueMessage
函数将消息入队,同时也持有Looper对象的引用,通过Looper.loop()
构建消息循环。Looper持有MessageQueue的对象引用,调用MessageQueue的next()
无限取出消息交给Handler的dispatchMessage()
函数处理
2.Looper可以调用MessageQueue提供的quit(boolean safe)
函数退出,但是主线程的 Looper 不允许退出。
3.子线程用Handler,必须调用Looper.prepare() 函数并且调用Looper.loop()或者在实例化的时候传入Looper.getMainLooper()。
4.ThreadLocal每个线程有单独一份存储空间,它具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
总结这篇也是因为最近在准备面试,希望能重新梳理一下Android的消息机制。
Handler面试总结
感谢
GityuanAndroid消息机制1-Handler(Java层)
AndyJenniferAndroid Handler机制
秉心说深入理解 Handler 消息机制