目录
从Handler.post()说起
Handler.post()
是用来发送消息的,我们看下Handler
源码的处理:
public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
首先会调用到getPostMessage()
方法将Runnable
封装成一条Message
,然后紧接着调用sendMessageDelayed()
方法:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
这里我们介绍下sendMessageDelayed()
的第二个参数delayMillis
,这个表示消息延时执行的时间,而post()
方法本身代表着非延迟执行,所以这里delayMillis
的值为0.
而如果是我们另一个常用的函数postDelay()
,这里的delayMillis
的值就是传入的延迟执行的时间
。
继续往下走,会调用到Handler.sendMessageAtTime()
方法:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; //... return enqueueMessage(queue, msg, uptimeMillis); }
获取到Looper
对应的消息队列MessageQueue
,继续往下走,作为参数传给enqueueMessage()
方法,这个方法主要是对上面封装的Message
进行填充:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
比如将Message
被负责分发的target
赋值成当前Handler
对象,然后根据是否为异步Handler
来决定是否给Message
添加异步标识。
MessageQueue.enqueueMessage()添加消息至队列中
boolean enqueueMessage(Message msg, long when) { //... synchronized (this) { //... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; //1. if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //2. for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } //3. if (needWake) { nativeWake(mPtr); } } return true; }
这个方法的使用很明确,就是将Message
添加到消息队列中,下来我们主要讲解这个方法的三个核心点,对应上面的注释标识:
1.如果当前消息队列本来为null、消息执行的时间戳为0、消息执行的时间小于消息队列队头消息的执行时间,只要满足上面三个条件之一,直接将该条Message
添加到消息队列队头;
这里说下消息执行的时间戳什么时候会为0,就是调用Handler.sendMessageAtFrontOfQueue()
这个方法,就会触发将当前发送的Message
添加到消息队列队头。
2.如果上面的三个条件都不满足,就遍历消息队列,比较将要发送的消息和消息队列的消息执行时间戳when
,选择适当的位置插入;
3.判断是否需要唤醒当前主线程,开始从消息队列获取消息进行执行;
Looper.loop()分发消息
这个方法会开启一个for(;;)循环
,不断的从消息队列中获取消息分发执行,没有消息时会阻塞主线程进行休眠,让出CPU执行权。
for(;;)循环
会不断的调用Looper.loopOnce()
,开始真正的消息获取和分发执行:
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block if (msg == null) { return false; } try { msg.target.dispatchMessage(msg); } msg.recycleUnchecked(); return true; }
上面是经过简化的代码,首先调用MessageQueue.next()
从消息队列中获取消息,然后调用关键方法msg.target.dispatchMessage(msg)
开始消息的分发执行,这个方法之前的文章有进行介绍,这里就不再过多介绍了。
接下来我们看下MessageQueue.next()
如何获取消息的。
MessageQueue.next()获取消息
Message next() { //... for (;;) { //1.休眠主线程 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //2.获取异步消息 if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } //3.获取普通消息 if (msg != null) { if (now < msg.when) { nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } else { nextPollTimeoutMillis = -1; } //... if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } //4.执行Idle消息 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; boolean keep = idler.queueIdle(); if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //... } }
- 如果当前消息队列中没有消息或者还没到下一条消息的执行时间,就调用
nativePollOnce()
方法休眠主线程,让出CPU执行权; - 如果
Message
的target为null,就代表是一个消息屏障消息,之后就只能从消息队列获取异步消息了,如果不存在,就尝试执行Idle
消息; - 如果不存在消息屏障,则就从消息队列中正常尝试获取
Message
,如果不存在,就尝试执行Idle
消息; - 执行
Idle
消息,只有在主线程空闲(当前消息队列中没有消息或者还没到下一条消息的执行时间)的情况下才会去尝试执行Idle
消息,这种类型的消息非常有用,具体的可以参考我之前写的文章:IdleHandler基本使用及应用案例分析
总结
本篇文章主要是详细分析了Android消息机制的整个执行流程(不包括native层),最核心的就是Handler
、Looper
、MessageQueue
、Message
四个类及构成的关联。