一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
b.quit()
void quit(boolean safe) {
//主线程的MessageQueue不允许退出
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
//加入同步锁,如果正在退出则直接返回
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
//唤醒锁
nativeWake(mPtr);
}
}
当执行Looper.quit()后,实际执行的是Message.quit(),在quit()里面设置mQuitting=true,最后执行nativeWake(mPtr)来唤醒next(),最终返回message为null,然后Looper.loop()里面检测到获取到的message为null,直接退出loop()。
c.next()
Message next() {
.........
for (;;) {
//-------------------分析点1---------------------------
nativePollOnce(ptr, nextPollTimeoutMillis);
//-------------------分析点2---------------------------
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//-------------------分析点3---------------------------
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) {
//-------------------分析点4---------------------------
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//-------------------分析点5---------------------------
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 {
//queue里面没消息时,就将nextPollTimeoutMillis置为-1,一直等待中
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//当执行quit()时,会执行以下逻辑
if (mQuitting) {
dispose();
//返回的message为null,Looper.loop()中的Message msg = queue.next()接收到message为null,直接return,从而退出loop()
return null;
}
........
........
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//-------------------分析点6---------------------------
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;
}
}
从上面代码可以看到,主要的点为:
分析点1:nativePollOnce(ptr, nextPollTimeoutMillis)是native方法,最终会调用到Linux的epoll_wait()进行阻塞等待,nextPollTimeoutMillis是等待时间,当消息队列中没有消息时,就一直等待,直到通过nativeWeak()来唤醒;有消息但是没有到消息的执行时间时,需要等待delay时间,然后执行下面的逻辑;
分析点2:加入synchronized (this)来确保取message的正确性;
分析点3:消息屏障,即:msg.target == null,只能通过postSyncBarrier()来插入屏障消息,不能通过enqueueMessage()(会判断msg.target==null,直接抛异常),当消息队列头是屏障信息时,会循环找到消息队列中的异步消息(asynchronous),如果找到,执行分析点4;如果未找到的话,会一直休眠,等待唤醒;
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) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
从暴露的方法来看是hide及private的,应用是调用不到的,除非通过反射调用,从hide的方法可以看到,调用该方法后,会将该屏障消息置于消息队列的头部,因为SystemClock.uptimeMillis()肯定小于当前时间的,然后返回token;
前面分析到,在消息队列中有消息屏障后,只会执行队列中的异步消息,那剩余的普通消息什么时候能够被执行呢?移除屏障消息:
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
//----------------------判断a----------------------------
if (prev != null) {
prev.next = p.next;
needWake = false;
//----------------------判断b----------------------------
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
根据上面返回的token来移除屏障消息,然后判断是否需要执行nativeWake()来唤醒next(),来执行队列中的普通消息,判断是否需要执行nativeWake()条件如下:
判断a:prev不为null,说明屏障消息不处于mMessages队列头,即还未执行到,不必执行唤醒;
判断b:prev为null,说明屏障消息处于mMessages队列头,如果mMessages中有普通消息的话肯定未执行,因此来判断如果next是普通消息或mMessages为空时,进行nativeWeak();如果不为null,且next还是屏障信息,不需要nativeWake()。
分析点4:当queue头的message未到执行时间时,会计算出nextPollTimeoutMillis,最后通过nativePollOnce(ptr, nextPollTimeoutMillis)等待时间到来执行message或新的nativeWeak();
分析点5:从queue头取出message,然后queue头指向下一个执行的message,最后返回取出的message;
分析点6:当队列中下一个需要执行的消息还未到时间(即需要等待nextPollTimeoutMillis),或消息队列中没有需要执行的消息(nextPollTimeoutMillis=-1),此时可以执行IdleHandler,即空闲的Handler,场景:1.延迟执行,没有固定时间;2.批量任务,任务密集,且只关注最终结果;使用方式如下:
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//do something
return false;
}
});