一 前言

阅读本文的前提是你已经知道什么是Handler、Looper、ThreadLocal等基本类。源码位置可以在这里搜索。

二 源码分析

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;

} 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) {

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;

}

MessageQueue内部组织Message的数据结构其实是个单链表,按照Message的when字段升序排列,我们主要看下是如何把Message挂到链表上的。

Message可分为2种,一种是同步消息一种是异步消息,可通过方法isAsynchronous来判断,两种消息的区别是,当消息链表中的头部是一个屏障的时候,这个屏障后面的同步消息都无法处理,但是异步消息不受影响。

我们正常使用当中的时候绝大多数时候都是同步消息。但是添加屏障的接口是私有API,也就说,除非反射,否则开发者是无法调用这个API的。

我们现在开始分析代码,相对重要的代码是下面这几行

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 {

// 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) {

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);

}

如果当前链表为null或者当前消息的触发时间(when)小于链表头的触发时间,那么直接把这个消息加到链表的头部,next指向原头部,如果当前next函数是阻塞的,那么要唤醒next函数。

如果当前链表并且msg的触发时间要比链表头部msg触发时间要大,那么就遍历这个链表,找到它应该在的位置(按when升序)。

这里面有个唤醒next 方法的策略,就是这个needWake变量,如果当前头部的msg是个屏障消息,并且next函数是阻塞的,并且msg是这个链表中触发时间最早的,那么需要唤醒next函数。我们来分析下这几个条件:

如果next没阻塞,那么无论msg是什么,我们都不需要唤醒next。

如果next阻塞,但是链表头部是非屏障消息,说明头部还不到时间执行,到了时间next会自动唤醒,所以手动不需要唤醒。

如果next 阻塞,头部是屏障消息,如果msg不是触发最早的异步消息,那么说明最早的异步消也在等待处理,next函数在指定时间差内会自动唤醒,此时不需要手动唤醒。如果msg是最早的异步消息,那msg后面有可能有异步消息,也有可能没有异步消息,在有异步消息的情况,要重新唤醒next,为了及时能处理当前msg,在没有的情况下也要唤醒next,因为此时next是永久阻塞,阻塞参数为-1。

我们分析下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;

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) {//如果头消息是个屏障消息

// 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会阻塞一个指定时间段

// 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 {//直接返回给Looper

// 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 {//如果没找到消息,那么next可能会陷入阻塞

// 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();

}

if (pendingIdleHandlerCount <= 0) {//如果没空闲任务做,那么next就会陷入阻塞,nextPollTimeoutMillis=-1的话,就永远阻塞。

// 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.

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陷入永久阻塞,否则的话,直接处理头部消息,如果头部消息还不到时间处理,那么next会被阻塞(when-now)这么久,否则的话,直接返回给Looper

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐