1. Android 消息机制
Android 的消息机制主要是指 Handler
的运行机制以及 Handler
所附带的 MessageQueue
和 Looper
的工作过程,这三者是一个整体。
1.1. 消息机制的模型
Handler
创建后,内部的Lopper
和MessageQueue
和Handler
协同工作。Handler
的post
或send
方法,会调用MessageQueue
的enqueueMessage
方法,把Runnable
或Message
放入到消息队列中。Looper
遍历消息队列,并将队列中的消息分发给对应的Handler
。Handler
的handleMessage
方法中处理该消息。
1.2. 消息机制的作用
将任务切换到没有指定的线程中去执行。因为 Android 规定,访问 UI 只能在主线程中进行,在子线程中则会抛出异常。
- UI 控件不是线程安全的,多线程并发访问会不可控,加锁会让 UI 访问的逻辑变得复杂,还会降低 UI 的访问频率。
ViewRootImpl
对 UI 的操作做了验证,这个验证工作是由ViewRootImpl
的checkThread()
来完成的。
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的插入
Handler
的 sendMessage()
方法将消息发送到这该函数中。消息会按时间顺序被添加到消息队列中。主要操作就是单链接的插入操作。
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
退出后,Handler
的send()
方法会返回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()
方法是一个死循环,唯一跳出方式是MessageQueue
的next()
方法返回null
。- 当
Looper
的quit()
方法被调用时,Looper
会调用MessageQueue
的quit()
方法 或者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)
方法,将消息标记为异步。 -
在处理消息的时候,如果遇到同步屏障,则立即暂停处理同步消息,转而优先处理异步消息,等异步消息处理完后,再继续处理同步消息。
-
同步屏障本质上也是一个消息(屏障消息),和普通消息一样会被加入到消息队列中排队。但是
target
为null
,因此同步屏障并不会执行,仅仅用于标记,表示后面可能会有异步消息,请优先处理他们。
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()
系列方法:判断消息队列里是否有等待中的Message
和Runnable
。removeXXX()
系列方法:从消息队列里移除等待中的Message
和Runnable
。
5.3. 处理消息
消息处理的优先级分别是 Message.callback
、Handler.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. 执行流程
- 如果当前待执行的消息为
null
,或者这条消息的执行时间未到,则可以准备执行IdleHandler
。 - 当
pendingIdleHandlerCount < 0
时,给pendingIdleHandlerCount
赋初值为IdleHandler
的总数。 - 将
mIdleHandlers
中的IdleHandler
全部拷贝到mPendingIdleHandlers
临时数组中用于遍历。 - 遍历
mPendingIdleHandlers
数组,取出每个IdleHandler
执行queueIdle()
,并把返回值存到keep
中。 - 当
keep
为false
时,从mIdleHandler
中永久移除当前执行完毕的IdleHandler
,反之则保留。 - 执行完全部的
IdleHandler
后:- 重置
pendingIdleHandlerCount
为0
,直到下一次next
方法调用,再讨论是否再执行IdleHandler
。 nextPollTimeoutMillis
赋值为0
, 下一轮循环,natievPollOnce()
将立即返回,并立即获取新的消息。
- 重置
6.3. IdleHandler 总结
- 消息队列为
null
或者消息未到执行时间,IdleHandler
才会执行。 IdleHandler
的执行时机不可控,如果消息队列一直有可处理的消息,则IdleHander
将一直得不到执行。IdleHandler
的addIdleHandler()
和removeIdleHandler()
都有synchronized
代码块,因此线程安全。IdleHandler
运行在哪个线程,取决于调用addIdleHandler()
的MessageQueue
与哪个线程相关联。IdleHandler
是同步执行,执行完全部的IdleHandler
可能耗时较长,期间消息队列可能发生变化,所以下一轮循环不阻塞,立即查看最新的消息队列。- 在一次
next()
调用中,IdleHandler
只会执行一遍,不会因为在死循环里面,而多次执行。直到下一次next()
调用,再讨论是否再执行IdleHandler
。