Android-Handler源码解析-MessageQueue
源码版本:
- Handler:SDK-31
导航:
成员变量
// Log的Tag
private static final String TAG = "MessageQueue";
// 是否是Debug
private static final boolean DEBUG = false;
// 此消息队列是否可以退出,true为可以退出。
@UnsupportedAppUsage
private final boolean mQuitAllowed;
// natvie 层的消息队列的标识
@UnsupportedAppUsage
private long mPtr; // used by native code
// 此消息队列的头元素
@UnsupportedAppUsage
Message mMessages;
// 此消息队列的所有IdleHandler,当此消息队列空闲时执行。
@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 添加的FileDescriptorRecord集合,FileDescriptorRecord记录了FileDescriptor、事件(文件输入、输出、错误)、监听,当文件产生相应事件时,回调监听。
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
// 等待执行的所有IdleHandler
private IdleHandler[] mPendingIdleHandlers;
// 此消息队列是否退出
private boolean mQuitting;
// 此消息队列是否是阻塞的
private boolean mBlocked;
// 下一个屏障token。
// 障碍由带有空target的消息表示,其arg1字段携带token。
@UnsupportedAppUsagejava
private int mNextBarrierToken;
说明:
MessageQueue
为什么需要持有Message
,因为MessageQueue
是Message
集合。Message
相关介绍,请看Android-Handler源码解析-Message。
创建MessageQueue
想要使用MessageQueue
,首先需要创建MessageQueue
,所以我们接下来看下它是如何被创建的。
new MessageQueue()
MessageQueue类
MessageQueue(boolean quitAllowed) {
// 记录是否允许退出
mQuitAllowed = quitAllowed;
// 初始化native层的消息队列,并记录其返回指针。
mPtr = nativeInit();
}
MessageQueue
构造方法,为默认的,只能同包下调用,目前只有的Looper
创建时调用了,我们来看下Looper
的创建。
Looper构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 创建消息队列
mThread = Thread.currentThread();
}
创建Looper
,可以通过Looper.prepare()
方法创建非主线程Looper
,它是允许退出的;可以通过Looper.prepareMainLooper()
方法创建主线程Looper
,它是不允许退出的。
说明:
Looper
创建相关介绍,请看Android-Handler源码解析-Looper-创建Looper。
小结
MessageQueue
的创建,只能通过Looper
进行创建。主线程Looper
,它是不允许退出的;非主线程Looper
,它是允许退出的。
入队Message
当通过Handler
发送消息的时候,最终会调用MessageQueue
的enqueueMessage()
方法将Message
入队,我们来看一下。
Handler->enqueueMessage()
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 调用MessageQueue的enqueueMessage()方法入队消息
return queue.enqueueMessage(msg, uptimeMillis);
}
当Handler
发送消息的时候,最终都会走到此Handler
的enqueueMessage()
方法,最终都是调用MessageQueue
的enqueueMessage()
方法进行入队消息。
说明:
Handler
发送消息相关介绍,请看Android-Handler源码解析-Handler-发送Message。
接下来,我们来看下MessageQueue
的enqueueMessage()
方法。
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
// 消息的目标Handler为空,抛出异常。
throw new IllegalArgumentException("Message must have a target.");
}
// 加锁,保证线程安全,因为入队Message操作(handler.send/post)可以在任何线程调用。
synchronized (this) {
if (msg.isInUse()) {
// 消息正在用(未被回收),再用则抛出异常。
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
// 退出中,再入队,则Log警告提示,并回收消息。
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; // 更新消息的when时刻
Message p = mMessages; // 当前消息,默认为消息队列的Head元素。
boolean needWake; // 是否需要唤醒线程
if (p == null || when == 0 || when < p.when) {
// 新的头,将Message插入到队首,如果阻塞,则唤醒事件队列。
// 说明:
// p == null,说明头为空,队列为空。
// when == 0,说明需要排在队列前面(handler.sendMessageAtFrontOfQueue()方法调用传入)。
// when < p.when,说明此Message的执行时刻比Head的早,所以要将Message插入到队首。
msg.next = p; // 此Message链接Head(将此Message插入到队首)
mMessages = msg; // Head为此Message
needWake = mBlocked; // 如果阻塞,则唤醒。
} else {
// 队列有数据,并且非强制排在前面,并且此Message的执行时刻比Head的晚,将Message插入到队列中间。
// 通常我们不需要唤醒事件队列,除非在队列的头部有一个屏障,并且消息(msg)是队列中最早的异步消息。
// 阻塞,并且是头是同步屏障消息,并且是头是同步屏障消息,并且消息(msg)是异步消息,则先为唤醒。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev; // 上一个元素
// 遍历队列,找到 Message 目标插入位置
for (;;) {
prev = p; // 上一个元素等于当前元素
p = p.next; // 当前元素等于下个元素,方便下次循环。
if (p == null || when < p.when) {
// 找到位置,退出循环。
// 说明:
// p == null,说明下个元素为空,到队尾了。
// when < p.when,说明此Message的执行时刻比下个元素的早;又因为走的是此else逻辑,所以此Message的执行时刻比上个元素的晚,所以要将Message插入到此位置。
break;
}
if (needWake && p.isAsynchronous()) {
// 需要唤醒,并且下个消息是异步消息,即消息(msg)不是队列中最早的异步消息,则不需要唤醒。
needWake = false;
}
}
// 将Message插入队列的目标位置
msg.next = p;
prev.next = msg;
}
// 是否需要唤醒Native侧的MessageQueue
if (needWake) {
// 唤醒
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()
方法,为向此MessageQueue
中入队消息,并判断是否需要唤醒。
说明:
- 在
Looper
退出后,再向MessageQueue
入队消息将不再入队,并返回false
。- 入队
Message
,是按Message
的when
升序进行入队(如果when
相同,按入队先后顺序),除非调用handler.sendMessageAtFrontOfQueue()
方法强制将消息入队到消息队列顶部。- 入队
Message
,需要判断是否需要唤醒,因为此线程之前可能进入阻塞,再入队消息,应该唤醒然后执行此消息,唤醒后的操作看后面的-获取Message。- 入队
Message
,并不是立即执行,如果是立即消息,则是等待此消息之前的消息(小于等于when
,小于说明之前的消息处理时耗时阻塞了,等于说明此消息加入的晚)处理完;如果是延时消息,则是等待时间到了便会执行。
小结
- 入队
Message
,是按Message
的when
升序进行入队(如果when
相同,按入队先后顺序),除非调用handler.sendMessageAtFrontOfQueue()
方法强制将消息入队到消息队列顶部。- 在
Looper
退出后,再向MessageQueue
入队消息将不再入队,并返回false
。
同步屏障
执行优先级高的Message
,可以通过调用handler.sendMessageAtFrontOfQueue()
方法强制将消息入队到消息队列顶部,但是这样不能保证此消息入队后到执行前其它消息没有继续入队到队列顶部,这样就不能保证首先执行。
使用同步屏障即可解决此问题,我们来看一下。
说明:
- 消息分为同步消息(
Message
的isAsynchronous()
为false
)、异步消息(Message
的isAsynchronous()
为true
)。- 同步屏障会屏障同步消息,只允许异步消息执行。
- 同步屏障功能,使用同步屏障消息(
Message
的target
为null
)来实现。
postSyncBarrier()
/** @hide */
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// 入队一个新的同步屏障token。
// 我们不需要唤醒队列,因为屏障的目的是让它停滞。
// 同步保证线程安全,因为可能在任意线程调用此方法。
synchronized (this) {
// 同步屏障消息可以有多个,用token区分,token通过mNextBarrierToken累加维护。
final int token = mNextBarrierToken++;
// 创建一个同步屏障消息(target为null)
final Message msg = Message.obtain();
msg.markInUse(); // 标记消息在用
msg.when = when; // 记录执行时刻
msg.arg1 = token; // 记录token
// 根据when排序找到插入位置。
Message prev = null; // 上个消息
Message p = mMessages; // 当前消息,默认为Head。
if (when != 0) {
while (p != null && p.when <= when) {
// 当前不为空,并且当前时刻小于目标时刻,继续查找。
prev = p; // 上个为当前
p = p.next; // 当前为下个,以便循环。
}
}
// 进行插入
if (prev != null) {
// 进入了while循环,找到了位置,将消息在此位置插入。
msg.next = p;
prev.next = msg;
} else {
// 未进入while循环,未找到了位置,将消息在队首插入。
// 未进入while循环,说明:队列为空,或队里的所有Message时刻都比屏障Message要晚。
msg.next = p;
mMessages = msg;
}
// 返回toke,为了移除用。
return token;
}
}
postSyncBarrier()
方法,为向此MessageQueue
中入队同步屏障消息,并返回token
用于移除此同步屏障。
说明:
postSyncBarrier()
方法为@hide
,只能系统调用;postSyncBarrier(long)
方法为private
的,所以我们只能通过反射来调用测试。- 创建一个同步屏障消息(
Message
的target
为null
),按照when
排序进行插入,保证了队列有序。- 同步屏障消息可入队多个,通过
token
来区分,通过此来移除。- 同步屏障消息只屏障此同步屏障消息
when
时刻后面的消息,when
时刻前面的会依次执行,详细见后面的-获取Message。- 同步屏障功能流程:1.同步屏障消息入队,2.异步消息入队,以完成此异步消息优先级最高执行功能,3.移除此同步屏障消息,否则此同步屏障消息入队之后的同步消息永远不会执行,详细见后面的-获取Message。
同步屏障消息入队后,需要进行调用removeSyncBarrier(int)
移除,否则此同步屏障消息when
时刻后面的消息永远不会执行,我们来看一下。
removeSyncBarrier()
/** @hide */
public void removeSyncBarrier(int token) {
// 从队列中删除同步屏障token。
// 如果队列不再被屏障阻碍,那么唤醒它。
synchronized (this) {
Message prev = null; // 上个消息
Message p = mMessages; // 当前消息,默认为Head。
// 遍历队列找到此token的屏障Message
while (p != null && (p.target != null || p.arg1 != token)) {
// 当前不为空,并且当前的不是同步屏障消息或者是同步屏障消息但是token不相等,继续查找。
prev = p;
p = p.next;
}
if (p == null) {
// 队列为空,或到末尾了没找到,抛出异常(指定的消息队列同步屏障token尚未发布或已被删除)。
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake; // 是否需要唤醒
// 进行移除
if (prev != null) {
// 进入了while循环,找到了位置,将消息在此位置移除。
prev.next = p.next;
needWake = false; // 不需要唤醒
} else {
// 未进入while循环,未找到了位置,将消息在队首移除。
// 未进入while循环,说明:队列为空,或队首就是此同步屏障消息。
mMessages = p.next;
// 如果队列为空,或队首不是同步屏障消息,则唤醒。
needWake = mMessages == null || mMessages.target != null;
}
// 回收消息
p.recycleUnchecked();
// 判断是否应该唤醒,如果loop退出(已经醒了),不需要再唤醒。
if (needWake && !mQuitting) {
// 唤醒
nativeWake(mPtr);
}
}
}
postSyncBarrier()
方法,为向此MessageQueue
中移除此token
的同步屏障消息。
说明:
removeSyncBarrier(long)
方法为@hide
,只能系统调用,所以我们只能通过反射来调用测试。- 如果此
MessageQueue
中,没有此token
的同步屏障消息,则抛出异常。
小结
- 同步屏障,屏障同步消息,只允许异步消息执行,为了实现此异步消息优先级最高执行功能。
- 同步屏障功能流程:1.同步屏障消息入队,2.异步消息入队,以完成此异步消息优先级最高执行功能,3.移除此同步屏障消息,否则此同步屏障消息入队之后的同步消息永远不会执行,详细见后面的-获取Message。
- 调用
postSyncBarrier()
方法入队一个同步屏障消息(Message
的target
为null
),并返回一个token
,用于调用removeSyncBarrier(long)
方法移除此同步屏障消息用。
IdleHandler
执行优先级低的任务,可以使用IdleHandler
实现,它会在MessageQueue
空闲时进行执行,以避免阻塞UI消息,导致页面卡顿,我们来看一下。
IdleHandler类
public static interface IdleHandler {
// 消息队列空闲,返回值代表此IdleHandler是否还是活跃的,返回ture代表是活跃的,返回false代表是非活跃的。
boolean queueIdle();
}
IdleHandler
接口的queueIdle()
方法,当MessageQueue
空闲(队列为空或者队列里的消息全部未到执行时刻)时进行调用通知,其返回值代表此IdleHandler
是否还是活跃的,返回ture
代表是活跃的(不在mIdleHandlers
中移除),返回false
代表是非活跃的(在mIdleHandlers
中移除),详细见后面的-获取Message。
addIdleHandler()
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
// IdleHandler为空,抛出异常(不能增加 null IdleHandler)。
throw new NullPointerException("Can't add a null IdleHandler");
}
// 同步保证线程安全,因为可能在任意线程调用此方法。
synchronized (this) {
// 在mIdleHandlers中添加
mIdleHandlers.add(handler);
}
}
addIdleHandler()
方法,为在mIdleHandlers
添加IdleHandler
。
说明:
- 增加的
IdleHandler
,如果没有进行移除(未调用removeIdleHandler()
方法或queueIdle()
方法返回true
),则始终会在MessageQueue
空闲时进行调用通知,详细见后面的-获取Message。
removeIdleHandler()
public void removeIdleHandler(@NonNull IdleHandler handler) {
// 同步保证线程安全,因为可能在任意线程调用此方法。
synchronized (this) {
// 在mIdleHandlers中移除
mIdleHandlers.remove(handler);
}
}
addIdleHandler()
方法,为在mIdleHandlers
移除此IdleHandler
。
小结
IdleHandler
,为MessageQueue
空闲(队列为空或者队列里的消息全部未到执行时刻)时进行执行,为了实现优先级最低执行功能。- 调用
addIdleHandler()
方法添加IdleHandler
,调用removeIdleHandler()
方法移除此IdleHandler
。
获取Message
各种消息(同步消息、异步消息、同步屏障消息)被添加到MessageQueue
后,便可以获取了,我们接下来看下它是如何被获取的。
next()
@UnsupportedAppUsage
Message next() {
// 如果消息loop已经退出并被销毁,则返回此处。
// 如果应用程序在退出后尝试重新启动looper,则可能会发生这种情况,这是不受支持的。
final long ptr = mPtr;
if (ptr == 0) {
// mPtr为0,说明loop已经退出了,直接返回null。
return null;
}
// 待处理的IdleHandler的数量,为-1代表是第一次初始化的状态。
int pendingIdleHandlerCount = -1;
// 下一次Poll超时时间,nativePollOnce()方法用。
int nextPollTimeoutMillis = 0;
// 开启循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞方法,根据nextPollTimeoutMillis判断是否要阻塞。
// nextPollTimeoutMillis=-1,一直阻塞,不会超时。
// nextPollTimeoutMillis=0,不会阻塞,立即返回。
// nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒,如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试检索下一条消息,如果发现则返回。
final long now = SystemClock.uptimeMillis(); // 当前时刻
Message prevMsg = null; // 上一个消息
Message msg = mMessages; // 当前消息,默认为Head。
if (msg != null && msg.target == null) {
// 队首是同步屏障消息,被屏障阻挡。在队列中查找下一个异步Message。
// 说明:同步屏障只允许异步消息通过,所以得找到下一个异步Message。
// 使用do-while先获取到下一个数据,然后再判断,因为当前消息为同步屏障消息。
do {
prevMsg = msg; // 前一个为当前
msg = msg.next; // 当前的为下一个,以便循环。
} while (msg != null && !msg.isAsynchronous()); // 消息不为空,并且是同步消息,全部过滤掉。
}
if (msg != null) {
// 有更多消息,判断是阻塞还是返回消息。
if (now < msg.when) {
// 未到达执行时刻。设置一个阻塞时间,以便到达时唤醒。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 到达执行时刻,移除此消息,并返回此消息。
mBlocked = false; // 不阻塞状态
// 移除此Message
if (prevMsg != null) {
// 进入了do-while循环,找到了位置,将消息在此位置移除。
// 进入了do-while循环,说明:队首是同步屏障消息,并且此消息是异步消息。
prevMsg.next = msg.next; // 移除操作
} else {
// 未进入了do-while循环,未找到了位置,将消息在队首移除。
// 未进入了do-while循环,并且msg不为null,说明:队首不是同步屏障消息,并且此消息就是队首。
mMessages = msg.next; // 移除操作
}
msg.next = null; // 断开链接
if (DEBUG) Log.v(TAG, "Returning message: " + msg); // 打印日志,提示消息返回。
msg.markInUse(); // 标记消息在用
return msg; // 返回此消息
}
} else {
// 无更多消息,让其一直阻塞。
nextPollTimeoutMillis = -1;
}
// 说明:走到这,说明消息队列为空,或者消息未到达执行时刻,已经设置了阻塞时间nextPollTimeoutMillis,以便下次for循环执行。
// 处理退出
if (mQuitting) {
// 正在退出,返回null,以结束Looper.loop()。
dispose();
return null;
}
// 获取等待执行的IdleHandler数量,需在第一次并且消息队列空闲时获取,只获取一次。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
// 第一次,并且消息队列空闲(队列为空或者当前时刻未到队首的执行时刻),获取IdleHandler数量。
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有IdleHandler要处理,则进入下一次for循环,以便执行设置好的nextPollTimeoutMillis。
mBlocked = true; // 设置为阻塞状态,原因看上面说明。
continue;
}
// 说明:走到这,说明消息队列空闲,并且有IdleHandler要处理。以下代码全部为处理IdleHandler。
// 创建mPendingIdleHandlers数组,最少为4个。
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// 将mIdleHandlers中所有IdleHandler,复制到mPendingIdleHandlers中,以便执行。
// 说明:如果mIdleHandlers数量比mPendingIdleHandlers数组长度大,则会进行扩容。
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 运行IdleHandler,我们只在此next方法,第一次迭代时到达这个代码块。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i]; // 获取IdleHandler
mPendingIdleHandlers[i] = null; // 释放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); // 移除
}
}
}
// 将IdleHandler数量重置为0,这样我们就不会再运行它们了。
pendingIdleHandlerCount = 0;
// 在调用IdleHandler时,可能已经发送了新消息,因此可以返回并再次查找等待的消息,而不必等待。
nextPollTimeoutMillis = 0;
}
}
next()
方法,为在MessageQueue
获取下一个Message
,如果此队列有Message
并且Message
已到执行时刻,则直接返回;如果此队列为空,或者此队列有Message
并且Message
未到执行时刻,则进行阻塞,以等待新的Message
加入,或者等待消息执行时刻到达。
说明:
Looper
退出后,next()
方法会返回null
,以结束Looper.loop()
。- 如果队首是同步屏障消息,则会过滤掉(不会删除)里面的同步消息,只允许异步消息执行。
- 同步屏障消息添加后,记得删除,否则同步消息永远不会执行。
MessageQueue
空闲状态,为队列为空,或者当前时刻未到队首的执行时刻。IdleHandler
的queueIdle()
方法,返回值为是否继续保持活跃状态,true
为保持活跃(不在mIdleHandlers
中移除),false
为不保持活跃(在mIdleHandlers
中移除),如果没有移除,则会在空闲时继续通知。next()
方法内的for (;;)
不是为了从此MessageQueue
中遍历消息,而是为了从阻塞状态到返回消息状态的判断转换。next()
方法可能会阻塞线程,enqueueMessage()
、quit()
、removeSyncBarrier()
方法可能会唤醒线程。
小结
- 获取
Message
,next()
方法会在MessageQueue
获取下一个Message
,如果此队列有Message
并且Message
已到执行时刻,则直接返回;如果此队列为空,或者此队列有Message
并且Message
未到执行时刻,则进行阻塞,以等待新的Message
加入,或者等待消息执行时刻到达。- 如果队首是同步屏障消息,则会过滤掉(不会删除)里面的同步消息,只允许异步消息执行。
IdleHandler
为MessageQueue
空闲状态(队列为空,或者当前时刻未到队首的执行时刻)执行,其queueIdle()
方法,返回值为是否继续保持活跃状态,true
为保持活跃(不在mIdleHandlers
中移除),false
为不保持活跃(在mIdleHandlers
中移除),如果没有移除,则会在空闲时继续通知。
移除Message
说明:
- 一个
MessageQueue
对应多个Message
,一个Message
对应一个Handler
,所以一个MessageQueue
可能对应多个Handler
(多个Handler
共用一个Looper
的情况)。- 一个
MessageQueue
可能对应多个相同Message
类型(Message
的target
、what
、obj
相同)或相同Callback
类型(Message
的target
、callback
、obj
相同)的消息。
removeMessages()
移除Messages
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
// Handler为空,直接返回,因为得需要Handler区分是哪个Handler的消息。
return;
}
// 加入同步锁,保证线程安全,因为Handler可能会在任意线程调用移除Message、Callback等方法。
synchronized (this) {
// 当前消息,默认为Head。
Message p = mMessages;
// 删除前面符合条件的所有Message(即队首是删除目标,从头开始删除,直到不符合为止)。
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
// 当前消息不为空,并且handler、what和目标相同,并且不匹配object或者匹配和目标相同,则移除当前消息。
Message n = p.next; // 下个消息
mMessages = n; // 因为要移除当前的,所以Head为下一个。
p.recycleUnchecked(); // 当前的消息回收
p = n; // 当前的为下一个,以便循环。
}
// 删除前面删除后剩余部分符合条件的所有Message。
while (p != null) {
Message n = p.next; // 下个消息
if (n != null) { // 有下个消息
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
// 下个消息handler、what和目标相同,并且不匹配object或者匹配和目标相同,则移除下个消息。
Message nn = n.next; // 下下个消息
n.recycleUnchecked(); // 下个消息回收
p.next = nn; // 下个消息为下下个消息,进行移除下个消息。
continue; // 当前的消息位置不变,以便再次进行判断是否下一个符合。
}
}
p = n; // 当前的为下个,以便循环寻找匹配上面if的。
}
}
}
removeMessages(Handler, int, Object)
方法,为删除此MessageQueue
中所有与参数相同Message
类型(Message
的target
、what
、obj
相同)的消息。
说明:
- 它会先删除前面符合条件的所有
Message
(即队首是删除目标,从头开始删除,直到不符合为止),后删除前面删除后剩余部分符合条件的所有Message
。- 参数
object
为空,为不匹配Message
的obj
,否则匹配是否和object
相同。Handler
的removeMessages()
方法,调用的就是此方法。Handler
移除消息相关介绍,请看Android-Handler源码解析-Handler-移除Messages、Callbacks。
移除Callbacks
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeMessages(Handler, Runnable, Object)
方法,为删除此MessageQueue
中所有与参数相同Callback
类型(Message
的target
、callback
、obj
相同)的消息。
说明:
- 它的删除过程和
removeMessages(Handler, int, Object)
方法相同,只是把原来的Message
的what
判断改为了Message
的callback
判断。Handler
的removeCallbacks()
方法,调用的就是此方法。
removeCallbacksAndMessages()
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeCallbacksAndMessages(Handler, Object)
方法,为删除此MessageQueue
中此Handler
发出的所有Message
、Callback
(Message
的target
、obj
相同)。
说明:
- 它的删除过程和
removeMessages(Handler, int, Object)
、removeMessages(Handler, Runnable, Object)
方法相同,只是把原来的Message
的what
判断或callback
判断去掉了。Handler
的removeCallbacksAndMessages()
方法,调用的就是此方法。- 可以调用
handler.removeCallbacksAndMessages(null)
删除此Handler
的所有Messages
、Callbacks
。
小结
- 移除
Message
,removeMessages(Handler, int, Object)
、removeMessages(Handler, Runnable, Object)
、removeCallbacksAndMessages(Handler, Object)
方法支持删除所有符合条件的Messages
、Callbacks
。Handler
的removeMessages()
、removeCallbacks()
、removeCallbacksAndMessages()
方法,就是分别调用的这些方法。
是否有Message
hasMessages()
是否有Message
boolean hasMessages(Handler h, int what, Object object) {
if (h == null) {
// Handler为空,直接返回。
return false;
}
// 加入同步锁,保证线程安全,因为Handler可能会在任意线程调用是否有Message、Callback等方法。
synchronized (this) {
// 当前消息,默认为Head。
Message p = mMessages;
// 遍历判断是否有
while (p != null) {
if (p.target == h && p.what == what && (object == null || p.obj == object)) {
// 当前消息handler、what和目标相同,并且不匹配object或者匹配和目标相同,则返回true。
return true;
}
p = p.next; // 当前的为下一个,以便循环。
}
return false; // 没找到,返回false。
}
}
hasMessages(Handler, int, Object)
方法,为判断此MessageQueue
中是否有与参数相同Message
类型(Message
的target
、what
、obj
相同)的消息。
说明:
- 它会遍历
MessageQueue
中的所有Message
,只要有一条符合条件,就返回ture
;否则返回false
。- 参数
object
为空,为不匹配Message
的obj
,否则匹配是否和object
相同。Handler
的hasMessages()
方法,调用的就是此方法。Handler
是否有消息相关介绍,请看Android-Handler源码解析-Handler-是否有Messages、Callbacks。
是否有Callback
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean hasMessages(Handler h, Runnable r, Object object) {
if (h == null) {
return false;
}
synchronized (this) {
Message p = mMessages;
while (p != null) {
if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
return true;
}
p = p.next;
}
return false;
}
}
hasMessages(Handler, Runnable, Object)
方法,为判断此MessageQueue
中是否有与参数相同Callback
类型(Message
的target
、callback
、obj
相同)的消息。
说明:
- 它的判断过程和
hasMessages(Handler, int, Object)
方法相同,只是把原来的Message
的what
判断改为了Message
的callback
判断。Handler
的hasCallbacks()
方法,调用的就是此方法。
是否有Messages或Callbacks
boolean hasMessages(Handler h) {
if (h == null) {
return false;
}
synchronized (this) {
Message p = mMessages;
while (p != null) {
if (p.target == h) {
return true;
}
p = p.next;
}
return false;
}
}
hasMessages(Handler)
方法,为判断此MessageQueue
中是否有此Handler
发出的所有Message
、Callback
(Message
的target
相同)。
说明:
- 它的判断过程和
hasMessages(Handler, int, Object)
、hasMessages(Handler, Runnable, Object)
方法相同,只是把原来的Message
的what
、obj
判断或callback
、obj
判断去掉了。Handler
的hasMessagesOrCallbacks()
方法,调用的就是此方法。
小结
- 是否有
Message
,hasMessages(Handler, int, Object)
、hasMessages(Handler, Runnable, Object)
、hasMessages(Handler)
方法支持判断是否有符合条件的Messages
、Callbacks
。Handler
的hasMessages()
、hasCallbacks()
、hasMessagesOrCallbacks()
方法,就是分别调用的这些方法。
退出MessageQueue
quit()
void quit(boolean safe) {
if (!mQuitAllowed) {
// 不允许退出(只有MainLooper不允许退出),再退出,抛出异常(主线程不运行退出)。
throw new IllegalStateException("Main thread not allowed to quit.");
}
// 加入同步锁,保证线程安全,因为Looper可能会在任意线程调用退出方法。
synchronized (this) {
if (mQuitting) {
// 已经退出中,再退出,不操作。
return;
}
// 标记退出中
mQuitting = true;
// 判断是否是安全的退出
if (safe) {
// 安全的,移除未来(当前时刻以后)的所有消息。
removeAllFutureMessagesLocked();
} else {
// 不安全的,移除所有消息。
removeAllMessagesLocked();
}
// 唤醒,防止在阻塞中。
nativeWake(mPtr);
}
}
quit()
方法,为退出此MessageQueue
,会标记mQuitting
为true
,参数safe
为是否安全退出,安全退出会调用removeAllFutureMessagesLocked()
方法,移除未来(当前时刻以后)的所有消息。不安全退出会调用removeAllMessagesLocked()
方法,移除所有消息。
说明:
- 不允许退出的
MessageQueue
,退出后会抛出异常。Looper
的quit()
、quitSafely()
方法,调用的就是此方法。Looper
退出相关介绍,请看Android-Handler源码解析-Looper-退出Looper。
我们先来看一下,不安全退出调用的removeAllMessagesLocked()
方法。
removeAllMessagesLocked()
private void removeAllMessagesLocked() {
// 当前消息,默认为Head。
Message p = mMessages;
// 遍历所有消息,然后进行回收。
while (p != null) {
Message n = p.next; // 下个消息
p.recycleUnchecked(); // 回收当前消息
p = n; // 当前为下个,以便循环。
}
// Head为空,断开和其它消息的链接,使队列为空。
mMessages = null;
}
removeAllMessagesLocked()
方法,为移除此MessageQueue
中所有消息。
我们再来看一下,安全退出调用的removeAllFutureMessagesLocked()
方法。
removeAllFutureMessagesLocked()
private void removeAllFutureMessagesLocked() {
// 当前时刻
final long now = SystemClock.uptimeMillis();
Message p = mMessages; // 当前消息,默认为Head。
if (p != null) {
// 队列有数据,进行移除。
if (p.when> now) {
// 队首的执行时刻大于现在的时刻,说明之后的消息都是未来(以后)执行的,所以移除所有消息。
removeAllMessagesLocked();
} else {
// 非全部未来(以后)执行,找到位置,进行移除之后的。
Message n; // 下个消息
// 遍历,找到移除始发位置。
for (;;) {
n = p.next; // 下个消息
if (n == null) {
// 没有下个消息,说明到队尾了,还未走下面if退出,说明所有消息都是以前的消息,直接返回。
return;
}
if (n.when > now) {
// 下个消息执行时刻大于当前时刻,再加上队列里的时刻是从小到大有序的,
// 则说明当前时刻,比当前消息执行时刻大,比下个消息执行时刻小,所以即找到位置,退出遍历。
break;
}
p = n; // 当前为下个,以便循环。
}
// 移除之后的
p.next = null; // 断开后续链接,即已经移除成功。
// 遍历之后所有消息,然后进行回收。
do {
p = n;
n = p.next;
p.recycleUnchecked(); // 回收消息
} while (n != null);
}
}
}
removeAllFutureMessagesLocked()
方法,为移除此MessageQueue
中未来(当前时刻以后)的所有消息。
小结
- 退出
MessageQueue
,为quit()
、quitSafely()
。quit()
为不安全退出,它会移除此MessageQueue
中所有消息。quitSafely()
为安全退出,它会移除此MessageQueue
中未来(当前时刻以后)的所有消息。
其它
isIdle()
public boolean isIdle() {
// 同步保证线程安全,因为可能在任意线程调用此方法。
synchronized (this) {
final long now = SystemClock.uptimeMillis(); // 当前时间
return mMessages == null || now < mMessages.when; // 队列为空,或者当前时刻未到队首的执行时刻。
}
}
判断此MessageQueue
,此时是否空闲状态。
OnFileDescriptorEventListener
addOnFileDescriptorEventListener()
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
}
addOnFileDescriptorEventListener()
方法,为添加FileDescriptor
监听器,以便在发生FileDescriptor
相关事件时接收通知。
removeOnFileDescriptorEventListener()
public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
}
removeOnFileDescriptorEventListener()
方法,为移除FileDescriptor
监听器。
添加和移除都调用了updateOnFileDescriptorEventListenerLocked()
方法,只不过移除的events
传入的时0
,我们来看下这个方法。
updateOnFileDescriptorEventListenerLocked()
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
int index = -1;
FileDescriptorRecord record = null;
if (mFileDescriptorRecords != null) {
index = mFileDescriptorRecords.indexOfKey(fdNum);
if (index >= 0) {
record = mFileDescriptorRecords.valueAt(index);
if (record != null && record.mEvents == events) {
return;
}
}
}
if (events != 0) {
// 添加
events |= OnFileDescriptorEventListener.EVENT_ERROR;
if (record == null) {
if (mFileDescriptorRecords == null) {
mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
}
record = new FileDescriptorRecord(fd, events, listener);
mFileDescriptorRecords.put(fdNum, record);
} else {
record.mListener = listener;
record.mEvents = events;
record.mSeq += 1;
}
nativeSetFileDescriptorEvents(mPtr, fdNum, events);
} else if (record != null) {
// 移除
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
}
}
dump()
void dump(Printer pw, String prefix, Handler h) {
synchronized (this) {
long now = SystemClock.uptimeMillis();
int n = 0;
for (Message msg = mMessages; msg != null; msg = msg.next) {
if (h == null || h == msg.target) {
pw.println(prefix + "Message " + n + ": " + msg.toString(now));
}
n++;
}
pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
+ ", quitting=" + mQuitting + ")");
}
}
为了调试目的,转储MessageQueue
的状态,打印此MessageQueue
内的此Handler
的所有Message
。
说明:
Looper
的dump()
方法,调用的就是此方法。Looper
转储相关介绍,请看Android-Handler源码解析-Looper-其它。
总结
以上就是Handler
源码的MessageQueue
源码部分,Handler
其它源码部分看下面导航。之后会出其它Android
源码系列,请及时关注。如果你有什么问题,大家评论区见!
导航:
最后推荐一下我的网站,开发者的技术博客: devbolg.cn ,目前包含android相关的技术,之后会面向全部开发者,欢迎大家来体验!