【Android进阶笔记】消息机制(Handler、Looper、MessageQueue、Message、IdleHandler)

428 阅读7分钟

1. Android 消息机制

Android 的消息机制主要是指 Handler 的运行机制以及 Handler 所附带的 MessageQueueLooper 的工作过程,这三者是一个整体。

1.1. 消息机制的模型

  • Handler 创建后,内部的 LopperMessageQueueHandler 协同工作。
  • Handlerpostsend 方法,会调用 MessageQueueenqueueMessage 方法,把 RunnableMessage 放入到消息队列中。
  • Looper 遍历消息队列,并将队列中的消息分发给对应的 Handler
  • HandlerhandleMessage 方法中处理该消息。

1.2. 消息机制的作用

将任务切换到没有指定的线程中去执行。因为 Android 规定,访问 UI 只能在主线程中进行,在子线程中则会抛出异常。

  • UI 控件不是线程安全的,多线程并发访问会不可控,加锁会让 UI 访问的逻辑变得复杂,还会降低 UI 的访问频率。
  • ViewRootImpl 对 UI 的操作做了验证,这个验证工作是由 ViewRootImplcheckThread() 来完成的。
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}


2. MessageQueue

  • MessageQueue 主要包含三个操作,插入、读取和退出。
  • 虽然叫做队列,但是内部实现并不是队列,实际上它是通过一个单链表来维护消息队列(对象池)。
  • 该单链表按照 Message 的执行时间,从头到尾依次增大来排列,即需要尽快处理的消息排在前面。
  • 最大容量为50,超出的 Message 则会挂起。

2.1. MessageQueue的插入

HandlersendMessage() 方法将消息发送到这该函数中。消息会按时间顺序被添加到消息队列中。主要操作就是单链接的插入操作。

boolean enqueueMessage(Message msg, long when) {
    /* ... */
    synchronized (this) {
        /* ... */
        msg.markInUse();
        msg.when = when;       // when是msg的正常运行时间
        Message p = mMessages; // mMessages相当于链表的头结点,它有next指针
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // 把msg插入到最前面的三种情况:
            // 1.当前链表为null,表示 msg 是第一条消息或所有消息已处理完。
            // 2.when = 0,表示有刻意的插队操作。
            // 3.when < 头结点的when,表示 msg 的运行时间 < 队列里面的所有消息
            msg.next = p;
            mMessages = msg; //true无消息,阻塞线程,false有消息,不阻塞线程
            // 由于插入到队头,即最新消息发生了变化,如果此时线程是在阻塞状态下,
            // 那就应该进行唤醒,以使其检查最新的消息是否是需要立刻处理
            needWake = mBlocked; // 线程是否需要唤醒,取决于当前线程是否阻塞
        } else {
            // 队头没有变化,插入的消息是要在队列中间的某个位置,一般不需要唤醒
            // 但如果 msg 是最早的异步消息,也是需要进行唤醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 遍历已存在的消息队列
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    // 找到一个位置,使得prev的when小于when,且p的when大于when
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 把msg插入到prev和p之间
            msg.next = p;
            prev.next = msg;
        }

        // 是否唤醒Looper等待的线程
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

2.2. MessageQueue的读取

next() 方法:是一个无限循环的方法,如果消息队列中没有消息,那么 next() 方法会一直阻塞在这里,当有新消息到来时,next() 方法会返回这条消息并将其从单链表中移除。

Message next() {
    /* ... */
    // nativePollOnce方法需要等待的时间:
    // -1 表示一直阻塞,不会超时
    // 0  表示不会阻塞,立即执行
    // >0 表示阻塞时长,即延迟多长时间发送消息
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // native的方法,在没有消息时会阻塞管道读取端,只有函数返回后才能往下执行
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // 从开机到现在的毫秒数
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages; // 待处理的消息,默认为链表的头结点
            if (msg != null && msg.target == null) {
                // 如果队头消息是一个屏障消息,即遇到了消息屏障
                // 则寻找队列中的第一个异步消息,优先执行
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
                // 经过循环后,如果有异步消息,则会赋值给msg,否则msg将为null
            }
            // 不管是队头消息,还是异步消息,下面的处理逻辑都一样
            if (msg != null) {
                // 消息队列有消息
                if (now < msg.when) {
                    // now小于msg.when则代表msg还未到发送消息的时间
                    // 虽然有消息,但是还没有到运行的时候
                    // 则计算还有等待多久,并赋值给nextPollTimeoutMillis
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // now大于等于msg.when则代表需要立即处理该条msg
                    mBlocked = false; // 代表当前没有阻塞
                    // 获取msg并且删除该结点 
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    // 返回待执行消息
                    return msg;
                }
            } else {
                // 消息队列没有消息,nextPollTimeoutMillis赋值为-1,表示一直阻塞
                nextPollTimeoutMillis = -1;
            }
            /* ... */
        }
        /* ... */
    }
}

2.3. MessageQueue的退出

Looper 中分别被 quit()quitSafely() 方法调用。

void quit(boolean safe) {
    /* ... */
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            //移除延迟消息,Looper.quitSafely()调用
            removeAllFutureMessagesLocked();
        } else {
            //移除全部消息,Looper.quit()调用
            removeAllMessagesLocked();
        }
        nativeWake(mPtr);
    }
}


3. Lopper

它会不停的从 MessageQueue 中查看是否有新消息,如果有新消息就会立即处理,否则就会一直阻塞在那里。

3.1. Looper的创建和退出

Handler 的工作需要 Looper,没有 Looper 的线程就会报错。在主线程创建 Handler 时,系统会为我们默认创建一个 Looper,而在子线程创建 Handler 时,就需要我们自己创建一个 Looper

new Thread("Thread #1") {
    @Override
    public void run() {
        Looper.prepare();
        Handler mHandler = new Handler();
        Looper.loop();
    }
}.start();

3.1.1. 创建 Looper

  • Looper.prepare():子线程创建 Looper 时使用。
  • Looper.parpareMainLooper():主线程(ActivityThread)创建 Looper 时使用。
private static void prepare(boolean quitAllowed) {
    /* ... */
    // 全局唯一的静态变量保存当前的Looper对象
    mThreadLocal.set(new Looper(quitAllowed));
}

3.1.2. Looper 的构造方法

final MessageQueue mQueue;  // final成员,关联到一个队列,且关联之后不能改变
final Thread mThread;       // final成员,关联到一个线程,且关联之后不能改变
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); // 创建消息队列
    mThread = Thread.currentThread();       // 保存当前线程对象
}

3.1.3. 退出 Looper

  • Looper.quit():直接立即退出。

  • Looper.quitSafely():把消息队列中已有消息处理完毕后才安全退出。

  • Looper 退出后,Handlersend() 方法会返回 false,消息发送失败。所在的线程也会立刻终止,因此建议不需要的时候终止 Looper

  • 在子线程中手动创建了 Looper,应该在所有的事情完成以后调用 quit 方法来终止循环,否则子线程会一直处于等待状态。

3.2. Looper的循环

只有调用了 Looper 的 loop() 方法后,消息循环系统才会真正工作。

public static void loop() {
    final Looper me = myLooper();
    /* ... */
    final MessageQueue queue = me.mQueue; // 获取队列
    /* ... */
    for (;;) { // 死循环处理消息
        // 从消息队列中获取消息,没有消息时,next方法会一直阻塞,不返回
        Message msg = queue.next(); 
        if (msg == null) {
            // 没有消息,则表示消息队列正在退出
            return;
        }
        /* ... */
        msg.target.dispatchMessage(msg); // 分发消息,targe就是Handler对象
        /* ... */
        msg.recycleUnchecked(); // 回收msg
    }
}
/* ... */
public static @Nullable Looper myLooper() {
    // 从全局唯一的mThreadLocal中get一个与当前线程关联的Looper对象
    // 如果调用线程未与Looper关联,则返回null 
    return sThreadLocal.get();
}
  • loop() 方法是一个死循环,唯一跳出方式是 MessageQueuenext() 方法返回 null
  • Looperquit() 方法被调用时,Looper 会调用 MessageQueuequit() 方法 或者 quitSafely() 方法来通知消息队列退出(消息队列被标记为退出状态,next() 方法就会返回 null )。
  • loop() 方法会调用 MessageQueue 的 next() 方法获取最新消息,而 next() 方法是一个阻塞操作,当没有消息时,next() 方法会一直阻塞,从而 loop() 方法也会阻塞。
  • next() 方法返回消息时,Looper 就会调用 msg.target.dispatchMessage(msg) 把 Message 对象又交给 Handler 来处理。


4. Message

4.1. Message 的主要成员

// 用户定义的消息代码,区分消息是关于什么的
public int what;
// 存很少的整形数据,可以使用arg1与arg2,
public int arg1;
public int arg2;
// 发送任意对象,跨进程时不能为null
public Object obj;
// 跨进程时,确定谁来接收
public Messenger replyTo;
// 跨进程时,确定发消息的uid
public int sendingUid = -1;

// 【标记】正在使用
static final int FLAG_IN_USE = 1 << 0;
// 【标记】异步消息
static final int FLAG_ASYNCHRONOUS = 1 << 1;
// 【标记】异步消息
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
// 消息的标记
@UnsupportedAppUsage
int flags;
// 消息的计划执行时间
@UnsupportedAppUsage
public long when;
// 传递大量的数据
Bundle data;
// Handler对象
@UnsupportedAppUsage
Handler target;
// Runnable对象
@UnsupportedAppUsage
Runnable callback;
// 回收对象池和消息队列中,链表的下一个结点
@UnsupportedAppUsage
Message next;

/** @hide */
// 从回收对象池中获取消息的同步锁对象,保证线程安全
public static final Object sPoolSync = new Object();
// 回收对象池
private static Message sPool;
// 回收对象池中回收的消息数量
private static int sPoolSize = 0;
// 回收对象池的最大容量是50(链表的最大长度是50)
private static final int MAX_POOL_SIZE = 50;
// 回收时是否校验已使用
private static boolean gCheckRecycle = true;

4.2. Message 的创建和获取

4.2.1. 创建 Message

// Message的构造方法是一个空实现
public Message() {
}

// 创建一个新的Message对象
Message message = new Message();

4.2.2. 获取 Message

Message 提供并重载了多个 obtain() 方法,来复用现有的无效 Message,减少创建和销毁的开销。

// 基础版obtain方法
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            // 如果回收对象池不为null,则取出回收对象池的队头元素
            Message m = sPool;
            sPool = m.next;
            // 断开与回收对象池的连接
            m.next = null;
            // 标记为正在使用
            m.flags = 0;
            // 可复用数量减1
            sPoolSize--;
            return m;
        }
    }
    // 回收对象池为空,则创建新的
    return new Message();
}
// 获取Message,并把现有Message的信息填充进去(深拷贝一个Message)
public static Message obtain(Message orig) {
}
// 获取Message,并重新指定Handler
public static Message obtain(Handler h) {
}
// ...还有很多个

4.3. 回收 Message

4.3.1. recycle() 方法

public void recycle() {
    // 如果该Message正在使用,且需要校验,则抛出异常,中断回收
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    // 执行回收
    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;
            sPool = this;
            sPoolSize++;	// 更新回收对象池数量
        }
    }
}

4.3.2. 回收时机

  • Handler 删除单条或所有消息时。

  • Looper 取出消息时。

  • Looper 取消循环消息队列时。

  • 当消息队列退出后,仍然发送消息过来。

4.4. 优先处理 Message

  • MessageQueue 中,Message 以链表形式存储,并根据 when 按由小到大进行排序。

  • Looper 循环并按先后顺序遍历 MessageQueue 获取 Message 并执行。

因此在链表中要做到优先执行,有两种方法:

  • Message 插入到 MessageQueue 这个链表的靠前位置,则优先执行。
    • 通过 sendMessage()postRunnable() 方法添加的消息,when 默认为当前时间戳,如果消息队列中存在 when 小于当前时间戳的消息,则该消息依然不会优先执行,只能保证相对靠前执行。
    • 通过 sendMessageAtFrontOfQueue()postAtFrontOfQueue() 方法添加的消息,when 默认为 0,直接添加到消息队列的队头位置,优先执行。
  • Looper 在获取 Message 的时候,暂停读取 MessageQueue 的第一个 Message,而是找出里面优先级最高的 Message 优先执行。

4.5. 同步屏障与异步消息

  • 我们构建出来的消息一般都是同步消息,如果要发送异步消息,则需要调用 Message 对象的 setAsynchronous(true) 方法,将消息标记为异步。

  • 在处理消息的时候,如果遇到同步屏障,则立即暂停处理同步消息,转而优先处理异步消息,等异步消息处理完后,再继续处理同步消息。

  • 同步屏障本质上也是一个消息(屏障消息),和普通消息一样会被加入到消息队列中排队。但是 targetnull,因此同步屏障并不会执行,仅仅用于标记,表示后面可能会有异步消息,请优先处理他们。

4.5.1. 发送和移除同步屏障

MessageQueue 类中,postSyncBarrier() 方法用来创建并发送同步屏障,removeSyncBarrier() 方法用来移除同步屏障。

// 发送同步屏障
@UnsupportedAppUsage
public int postSyncBarrier() {
    // when默认为当前时间戳
    return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
    synchronized (this) {
        final int token = mNextBarrierToken++;
        // 构建屏障消息,但是并没有对target赋值,可以用来区分
        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) {
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        // 返回token,用来解除同步屏障
        return token;
    }
}

由于 postSyncBarrier()removeSyncBarrier() 都有 @UnsupportedAppUsage 注解,因此我们不能直接通过 MessageQueue 对象直接调用,而是通过反射来调用。

// 发送消息屏障
public int postSyncBarrier() {
    Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
    return (int) method.invoke(Looper.getMainLooper().getQueue());
}
// 解除消息屏障
public void removeSyncBarrier(int token) {
    Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
    method.invoke(Looper.getMainLooper().getQueue(), token);
}

4.5.2. 获取同步屏障和异步消息

MessageQueue next() 方法中。

Message next() {
    /* ... */
    for (;;) {
        /* ... */
        synchronized (this) {
            // 从开机到现在的毫秒数
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages; // 待处理的消息,默认为链表的头结点
            if (msg != null && msg.target == null) {
                // 如果队头消息是一个屏障消息,即遇到了消息屏障
                // 则寻找队列中的第一个异步消息,优先执行
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
                // 经过循环后,如果有异步消息,则会赋值给msg,否则msg将为null
            }
            // 不管是队头的普通消息,还是异步消息,下面的处理逻辑都一样
            if (msg != null) {
                // 计算阻塞时间或者返回消息
                /* ... */
            } else {
                // 消息队列没有消息,nextPollTimeoutMillis赋值为-1,表示一直阻塞
                nextPollTimeoutMillis = -1;
            }
            /* ... */
        }
        /* ... */
    }
}

4.6. 总结 Message 的处理逻辑

  • 异步消息和同步消息一样,最终都是由自身 target 处理。
  • 添加了消息屏障之后,如果不移除,将会导致消息队列中的全部同步消息一直得不到处理,可能引起 ANR。
  • 异步消息虽然比同步消息具有优先执行权,但是异步消息的 when 默认是当前时间戳,而通过 sendMessageAtFrontOfQueue() 发送的插队消息,when 默认是 0,因此仍然会优先于异步消息执行。


5. Handler

Handler 可以发送和处理与某线程的 MessageQueue 相关联的 Message / Runnable 对象。

主要用途

  • 在未来某个时间点处理 Messages 或者执行 Runnables。
  • 将一段逻辑切换到另一个线程执行。

Handler 的构造方法

final Looper mLooper;       // final成员,关联到一个Looper,且关联之后不能改变
final MessageQueue mQueue;  // final成员,关联到一个队列,且关联之后不能改变
public Handler(Callback callback, boolean async) {
    /* ... */
    mLooper = Looper.myLooper();
    /* ... */
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

一个 Handler 只能关联一个 Looper,但是一个 Looper 可以关联多个 Handler。

5.1. 发送消息

  • postXXX 系列方法用于将 Runnable 对象加入消息队列。
  • sendXXX 系列方法用于将 Message 对象加入消息队列。
public final boolean post(Runnable r) {
    // postXXX 系列方法,最后也是和 Message 一样加到 MessageQueue
    return  sendMessageDelayed(getPostMessage(r), 0);
}
/* ... */
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r; 
    return m;
}
/* ... */
public final boolean sendMessage(Message msg) {
    // 立即发送消息,就是发送一条延时为0的延时消息
    return sendMessageDelayed(msg, 0);
}
/* ... */
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // 计算这条 Message 的 正常运行时间(开机至今的毫秒数 + 延时毫秒数)
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/* ... */
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    /* ... */
    return enqueueMessage(queue, msg, uptimeMillis);
}
/* ... */
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 把自身这个 Handler 对象塞到 Message 对象里面,供 Looper 调用
    msg.target = this;
    /* ... */
    return queue.enqueueMessage(msg, uptimeMillis);
}

5.2. 插队和撤回

  • sendMessageAtFrontOfQueue() 方法:将 Message 插入到消息队列的队头。
  • postAtFrontOfQueue() 方法:将 Runnable 插入到消息队列的队头。
  • hasXXX() 系列方法:判断消息队列里是否有等待中的 MessageRunnable
  • removeXXX() 系列方法:从消息队列里移除等待中的 MessageRunnable

5.3. 处理消息

消息处理的优先级分别是 Message.callbackHandler.mCallback,最后才是 Handler.handleMesage 方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        // msg.callback,即 handler.post(Runnable r) 的中的 Runnable 对象
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // mCallback ,即 new Handler(new Handler.Callback()) 的中的 Callback 对象
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 最后才是调用 Handler 的自身 handleMessage 方法
        handleMessage(msg);
    }
}
/* ... */
private static void handleCallback(Message message) {
    message.callback.run();
}


6. IdleHandler

6.1. 定义和使用

IdleHandler 是被定义在 MessageQueue.java 中的内部接口。为我们提供了一种,可以在 Looper 事件循环出现空闲的时候,允许我们执行任务的机制。

public static interface IdleHandler {
    // 返回 true 表示是一个持久的IdleHandler,会重复使用
    // 返回 false 表示是一个一次性的IdleHandler,用完即销毁
    boolean queueIdle();
}

具体使用方法

// 实现MessageQueue.IdleHandler接口,定义空闲任务
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler {
    @Override
    public boolean queueIdle() {
        // TODO: 具体的任务
        // 如果希望单次执行,则返回false,例如初始化任务
        // 如果希望多次执行,则返回true,例如跟踪任务
        return false;
    }
}
// 添加任务
Looper.myQueue().addIdleHandler(idleHandler);
// 移除任务
Looper.myQueue().removeIdleHandler(idleHandler);

6.2. IdleHandler 原理

6.2.1. 源码分析

public final class MessageQueue {
    /* ... */
    // 存储全部的IdleHandler任务
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    // 临时存储全部的IdleHandler任务
    private IdleHandler[] mPendingIdleHandlers;
    /* ... */
    
	// 添加IdleHandler任务
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
    
    // 移除IdleHandler任务
    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
    /* ... */
    
    Message next() {
        /* ... */
        // 挂起的IdleHandler的数量,即IdleHandler的数量
        // -1表示还没有初始化这个数量,后面会就行赋值
        int pendingIdleHandlerCount = -1;
        // nativePollOnce方法需要等待的时间:
        // -1表示一直阻塞切不会超时
        // 0表示不会阻塞,立即执行
        // >0 表示阻塞时长,即延迟多长时间发送消息
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // native的方法,在没有消息时会阻塞管道读取端,只有函数返回后才能往下执行
            nativePollOnce(ptr, nextPollTimeoutMillis);
            
            synchronized (this) {
                
                /* ...省略的这部分代码就是去查找合适的Message并返回... */

                if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                    // pendingIdleHandlerCount未初始化,并且当前消息队列为空或者消息还未到执行时间
                    // 则初始化IdleHandler的数量
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 如果没有IdleHandler,则立刻进入阻塞状态
                    mBlocked = true;
                    continue;
                }
				// 如果有IdleHandler,则把全部IdleHandler保存到mPendingIdleHandlers数组中
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            // 遍历全部IdleHandler,一个一个处理
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // 释放数组对当前IdleHandler的引用
				// 是否需要保留
                boolean keep = false;
                try {
                    // 执行IdleHandler,并根据返回值觉得是否保留
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
				// 如果不保留,则将执行完后的IdleHandler从数组中移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // 全部IdleHandler执行完毕后
            // 重置IdleHandler的数量为0,直到下一次next方法调用,再讨论是否再执行IdleHandler
            pendingIdleHandlerCount = 0;
            // nextPollTimeoutMillis赋值为0,表示不阻塞,随后立即下一轮循环
            nextPollTimeoutMillis = 0;
        }
    }
}   

6.2.2. 执行流程

  1. 如果当前待执行的消息为 null,或者这条消息的执行时间未到,则可以准备执行 IdleHandler
  2. pendingIdleHandlerCount < 0 时,给 pendingIdleHandlerCount 赋初值为 IdleHandler 的总数。
  3. mIdleHandlers 中的 IdleHandler 全部拷贝到 mPendingIdleHandlers 临时数组中用于遍历。
  4. 遍历 mPendingIdleHandlers 数组,取出每个 IdleHandler 执行 queueIdle() ,并把返回值存到 keep 中。
  5. keepfalse 时,从 mIdleHandler 中永久移除当前执行完毕的 IdleHandler,反之则保留。
  6. 执行完全部的 IdleHandler 后:
    • 重置 pendingIdleHandlerCount0 ,直到下一次 next 方法调用,再讨论是否再执行 IdleHandler
    • nextPollTimeoutMillis 赋值为 0, 下一轮循环,natievPollOnce() 将立即返回,并立即获取新的消息。

6.3. IdleHandler 总结

  • 消息队列为 null 或者消息未到执行时间,IdleHandler 才会执行。
  • IdleHandler 的执行时机不可控,如果消息队列一直有可处理的消息,则 IdleHander 将一直得不到执行。
  • IdleHandleraddIdleHandler()removeIdleHandler() 都有 synchronized 代码块,因此线程安全。
  • IdleHandler 运行在哪个线程,取决于调用 addIdleHandler()MessageQueue 与哪个线程相关联。
  • IdleHandler 是同步执行,执行完全部的 IdleHandler 可能耗时较长,期间消息队列可能发生变化,所以下一轮循环不阻塞,立即查看最新的消息队列。
  • 在一次 next() 调用中, IdleHandler 只会执行一遍,不会因为在死循环里面,而多次执行。直到下一次 next() 调用,再讨论是否再执行 IdleHandler


7. 消息机制方法调用总结