Handler是我们再熟悉不过的概念,我们经常使用它来进行子线程与主线程之间的通信。它又如此的重要,以至于HandlerThread,EventBus,RxJava,Glide等等开源框架都有它的身影。
这篇文章主要分析Handler及其相关类Looper、Message和MessageQueue等工作流程,还会讲到native层机制,内存屏障,IdleHandler等。
源码分析
我们从平时使用Handler的代码开始分析Handler底层的逻辑
// 只用来演示,不简易这样使用,会导致内存泄漏
val handler = @SuppressLint("HandlerLeak")
object : Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
// todo 处理消息
}
}
val message = Message.obtain()
message.what = 1
handler.sendMessage(message)
handler.post(object : Runnable{
override fun run() {
// todo 处理消息
}
})
使用方式很简单,主要有下面4步:
- 创建Handler实例,重写handleMessage方法
- 创建要发送的Message实例
- 发送消息,可以使用sendMessage,也可以使用post方式
- 接收到消息
- 如果是sendMessage发送的消息则在handleMessage中接收到
- 如果是post发送的消息,则会回调Runnable的run方法
下面会详细的对这4个步骤进行源码分析
1. 创建Handler实例
先从handler的构造函数开始,我们平时使用最多的就是无参构造,实际内部又调用的是有参构造。默认callback赋值为null,async赋值为false。
我们也可以选择有参构造
- Callback是Handler内部的回调接口,如果设置了则派发消息时,handleMessage会被回调。
- async表示是否是异步消息,默认是false,表示是同步消息
- Looper是MessageQueue中Message的管理者,具体用处后面再讲
public Handler() {//无参构造
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();// 获取looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;// 获取queue对象
mCallback = callback;// handler无参构造时callback为null
mAsynchronous = async;
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
...
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
/**
* Subclasses must implement this to receive messages
* 子类一定要实现该方法来接收消息
*/
public void handleMessage(@NonNull Message msg) {
}
2. 创建Message
Message是单列表结构,多个Message通过next引用连接起来,用于实现消息池。因为android会频繁的使用Message的对象,使用“池”这种机制可以减少创建对象开辟内存的时间,更加高效的利用内存。
public final class Message implements Parcelable {
public int what;
public Object obj;
public int arg1;
public int arg2;
……
public long when; // 消息执行的时间戳
Handler target; //每个消息都有一个成员保存和他关联的Handler
Runnable callback;// 回调,用于post发送消息的方式
Message next;//Message是单链表结构,指向下一个Message
public static final Object sPoolSync = new Object();// 锁对象
private static Message sPool;// 静态对象,消息池的head节点
private static int sPoolSize = 0;// 消息池的大小
private static final int MAX_POOL_SIZE = 50; // 消息池的最大容量
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
}
建议使用obtain来创建Message实例,优先从池内取出对象,先判断头节点sPool是否为空,不为空则返回;为空则说明当前池内没有节点,则创建新的Message对象。
Message使用完后需要调用recycle进行手动回收,类似于Bitmap的回收。
public void recycle() {
...
recycleUnchecked();
}
@UnsupportedAppUsage
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;// 把当前message插入到最前面
sPool = this;
sPoolSize++;
}
}
}
将Message中的成员变量赋值为初始值,如果消息池的大小没有超过最大容量(50),则放入消息池中进行回收。
3. 发送消息
Handler发送消息有两种方式:
- sendMessage
- post
另外还可以发送延迟消息:
- sendMessageDelayed
- postDelayed
3.1 sendMessage
我们先看下sendMessage方法
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
// 注意当前时间用的是SystemClock.uptimeMillis(),而不是System.currentTimeMillis
// 第二个参数为消息执行的时间,由当前时间+延迟的时间组成,不延迟时delayMillis为0
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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;// 重要:msg的target设置为该handler对象
if (mAsynchronous) {// 不设置默认是false,则该消息为同步消息
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessage方法通过层层调用,最后调用了enqueueMessage方法,在这个方法里,先是给msg的target设置为该handler对象,默认mAsynchronous为false,则消息为同步消息。关于同步消息和异步消息的用不后面再讲。
最后调用了queue的enqueueMessage方法。
System.currentTimeMillis()与SystemClock.uptimeMillis().md
3.2 post
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
原来post内部也是通过sendMessageDelayed的方式实现的。通过getPostMessage创建Message对象,不同点在于将我们创建的Runable对象作为callback属性,赋值给了此message,该callback会在派发消息的时候回调
发送消息的两种方式讲完了,消息如何进入MessageQueue的,需要看下MessageQueue类的enqueueMessage方法
3.3 MessageQueue#enqueueMessage
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;
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;// 如果blacked为ture则表示需要唤醒
} 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) {// 找到了when比当前Message的when大的Message
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;
}
整个enqueueMessage方法的过程就是先持有MessageQueue.this锁,然后将Message放入队列中,放入队列的过程是:
-
如果队列为空,或者当前处理的时间点为0(when的数值,when表示Message将要执行的时间点),或者当前Message需要处理的时间点先于队列中的首节点,那么就将Message放入队列首部,否则进行第2步。由于是插入到首节点,如果此时阻塞了,则需要唤醒。
-
遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后。所以消息队列中根据消息执行时间消息是从小到大排列的。插入到队列中间一般不需要唤醒,除非设置了同步屏障并且该消息是最早的异步消息。
-
判断是否需要唤醒,一般是当前队列为空的情况下,next那边会进入睡眠,需要enqueue这边唤醒next函数。后面会详细介绍
4. 接收消息
我们发送到MessageQueue中的消息是怎么被我们接收到的呢?答案是Looper。Looper作为MessageQueue的管理者,不停的在MessageQueue中找到要处理的Message并发送给我们,这样我们就接收到了。
既然需要Looper,但是我们平时使用的时候,为什么不需要创建Looper实例呢? 那是由于app启动创建主线程(ActivityThread)的时候,在main方法中已经帮我们创建了Looper对象,并且调用了loop()方法。所以我们平时在主线程使用的时候就不需要手动创建了。
...
Looper.prepareMainLooper();
...
Looper.loop();
...
但是在子线程使用的话,就需要手动调用prepare创建Looper实例,并调用loop方法。那是由于Looper是和线程绑定的,当线程中没有Looper对象的时候,就需要我们手动创建。
我们先来看下Looper对象的创建过程,Looper的构造函数是私有的,Looper对象必须通过prepare方法来创建。 创建好的Looper对象被保存在TheadLocal中,作用是为了保证每个线程都只有一个Looper对象。此外在Looper构造函数中创建了MessageQueue对象。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {// 一个线程只有一个Looper对象
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));// 创建Looper对象,并存放在ThreadLocal中
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);// 创建MessageQueue对象
mThread = Thread.currentThread();
}
Looper对象是和线程绑定在一起的,一个线程对应着一个Looper,并且一个Looper有对应着一个MessageQueue。
我们再来看下MessageQueue的构造函数,构造函数很简单,最重要的就是调用了nativeInit(),这个函数后面还会再讲,现在先简单介绍下,nativeInit是主要用来进行native层初始化,返回native层的nativeMessageQueue的指针mPtr,这个指针后面还会用到。
// MessageQueue的构造函数
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();// native层的初始化,返回native层的nativeMessageQueue的指针
}
我们再看下Looper的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;// 拿到Looper中的MessageQueue
// 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 (;;) {
// 通过MessageQueue的next()方法拿消息,可能就阻塞在这里
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 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.dispatchMessage(msg);// 调用发送消息的handler的dispatchMessage来处理消息
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();//回收
}
}
拿到当前线程所绑定的Looper对象中的queue,在for死循环中通过queue.next()方法拿到要处理的Message,当没有需要处理的消息时,则阻塞在queue.next()处;如果拿到了消息会被唤醒,则调用msg.target.dispatchMessage(msg),target是Message绑定的Handler对象,此时就把Message交给了Handler来处理,具体处理流程后面再讲。
接着看queue.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.
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(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) {//target 正常情况下都不会为null,在postBarrier会出现target为null的Message
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());// 遍历queue找到异步消息
}
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;// 没有消息,-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.
// queue中没有msg,或者msg还没到可执行的时间
// 那么线程就处于空闲了,就可以执行IdleHandler了
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;// 置为ture
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.
// 执行IdleHandler
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) {
// 如果之前的idler.queueIdle()返回false,则执行完后移除掉
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函数的主要是执行步骤是:
-
初始化操作,如果mPtr为null,则直接返回null,设置nextPollTimeoutMillis为0,进入下一步。
-
调用nativePollOnce, nativePollOnce有两个参数,第一个为mPtr表示native层MessageQueue的指针,nextPollTimeoutMillis表示超时返回时间,调用这个nativePollOnce会阻塞的此处等待唤醒,如果超过nextPollTimeoutMillis时间,则不管有没有被唤醒都会返回。-1表示一直等待,0表示立刻返回。下一小节单独介绍这个函数。
-
获取队列的头Message(msg),如果头Message的target为null,则表示发送了同步屏障,则优先查找一个异步Message来进行下一步处理。当队列中添加了同步Barrier的时候target会为null。
-
判断上一步获取的msg是否为null,为null说明当前队列中没有msg,设置等待时间nextPollTimeoutMillis为-1。实际上是等待enqueueMessage的nativeWake来唤醒,执行step4。如果非null,则下一步
-
判断msg的执行时间(when)是否比当前时间(now)的大,如果小,则将msg从队列中移除,并且返回msg,结束。如果大则设置等待时间nextPollTimeoutMillis为(int) Math.min(msg.when - now, Integer.MAX_VALUE),执行时间与当前时间的差与MAX_VALUE的较小值。执行下一步
-
判断是否MessageQueue是否已经取消,如果取消的话则返回null,否则下一步
-
运行idle Handler,idle表示当前有空闲时间的时候执行,而运行到这一步的时候,表示消息队列处理已经是出于空闲时间了(队列中没有Message,或者头部Message的执行时间(when)在当前时间之后)。如果没有idle,则继续step2,如果有则执行idleHandler的queueIdle方法,我们可以自己添加IdleHandler到MessageQueue里面(addIdleHandler方法),执行完后,回到step2。
回过头来看下Handler#dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);// 调用runnable.run()方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {// 如果callback不为null,则调用callback的handlerMessage
return;// 返回
}
}
handleMessage(msg);// 最后才会调用到handleMesaage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
第2行,如果msg.callback不为null,则执行callback回调,msg.callback就是通过post发送消息传递的Runnable对象
Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截(返回false),那么就意味着一个消息可以同时被 Callback 以及handleMessage 处理。
如果mCallback为null,则只会调用handleMessage方法
到这里,我们终于整个流程分析完了。
native机制
nativePollOnce与nativeWake底层使用了linux中的pipe管道来进行进程间的通讯,通过epoll机制实现的等待和唤醒。
管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章Android学习启动篇推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。 Android应用程序消息处理机制(Looper、Handler)分析
- nativeInit()方法:
- 创建了NativeMessageQueue对象,增加其引用计数,并将NativeMessageQueue指针mPtr保存在Java层的MessageQueue
- 创建了Native Looper对象
- 调用epoll的epoll_create()/epoll_ctl()来完成对mWakeEventFd和mRequests文件描述符的可读事件监听
- nativeDestroy()方法
- 调用RefBase::decStrong()来减少对象的引用计数
- 当引用计数为0时,则删除NativeMessageQueue对象
- nativePollOnce()方法
- 调用Looper::pollOnce()来完成,空闲时停留在epoll_wait()方法,用于等待事件发生或者超时
- nativeWake()方法
- 调用Looper::wake()来完成,向管道mWakeEventfd写入字符,等待的线程会被唤醒;
MessageQueue通过mPtr变量保存NativeMessageQueue对象,从而使得MessageQueue成为Java层和Native层的枢纽,既能处理上层消息,也能处理native层消息;下面列举Java层与Native层的对应图
同步屏障
handler中消息分为同步消息和异步消息,我们平时使用sendxx或者postxx方法发送的消息都是同步消息。
同步屏障就是同步消息的屏障,让异步消息优先执行。在MessageQueue#next方法的时候已经分析过。
典型的应用场景是:
Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 设置同步障碍,确保mTraversalRunnable优先被执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 内部通过Handler发送异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Choreographer.postCallback
->postCallbackDelayed
->postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);// 异步消息
mHandler.sendMessageAtTime(msg, dueTime);
}
}
mTraversalRunnable调用了performTraversals执行measure、layout、draw
为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障
下面看下MessageQueue#postSyncBarrier:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
// 循环消息链表,找到第一个when大于当前时间的消息p
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 把msg插入到p前面
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
创建了Message实例,并将它按照when从小到大顺序插入到合适的位置。
同步屏障消息和普通消息最大的不同的是没有设置target,也就是target=null。所以在MessageQueue#next遍历的时候,target=null的消息就是同步屏障。
IdleHandler
IdleHandler会线程消息队空闲时执行,可以用来提升性能。
比如在LeakCanary中使用IdleHandler在空闲的时候执行GC操作,监测对象没有被回收。
具体执行过程在MessageQueue#next中也已经分析过了,下面看下具体用法:
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// todo
return false;
}
});
queueIdle返回值决定着IdleHandler会被执行一次还是多次,如果返回false,则执行完后便会被移除掉。
面试问题
1. 主线程的死循环一直运行是不是特别消耗CPU资源呢?
并不是,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质是同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
总结
借用一张图总结下Handler的流程