Handler机制之MessageQueue源码分析
程序员文章站
2022-07-14 17:49:57
...
介绍:
一个用于保存(被Looper分发的)Message列表的低级类。与Native world的MessageQueue由紧密联系
MessageQueue类内部实现了两个Interface,一个静态内部类。
-
接口IdleHandler在消息队列没有消息时使用,处理poll状态时的动作
-
接口OnFileDescriptorEventListener在相应的文件状态改变(可读,可写,有错误)时被使用
-
静态内部类FileDescriptorRecord,记录相应文件状态改变时的监视器OnFileDescriptorEventListener,在被native方法调用的dispatchEvents方法里被调用,执行监视器
先来了解一下构造方法和属性:
//消息队列是否可以停止的标示,主线程是不可以停止,异步线程该可以停止,该标示为true
private final boolean mQuitAllowed;
// native code(C++)层的引用 ,当为0,则停止了该消息队列
private long mPtr;
//当前消息队列的头信息(即需要最先处理的信息)
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
//用于标示next()方法是否阻塞,等待pollOne()非零时间
private boolean mBlocked;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//调用native层创建相应的C++类对象,返回引用
mPtr = nativeInit();
}
接下来,了解Handler.sendMessage()
传入Message,最终调用到enqueueMessage()
。
boolean enqueueMessage(Message msg, long when) {
//message的target为空,即Handler为空,会跑出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//Message若是在使用期间,会被跑出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//消息队列已经停止,则停止新加入message
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;
}
//标记该message在使用状态
msg.markInUse();
msg.when = when;
//获取到上一个Message
Message p = mMessages;
//是否唤醒的标示
boolean needWake;
//消息队列中没有信息,或者需要立即执行的message,或者当前消息的处理时间小于消息队列中头消息的处理时间,则将当前信息作为消息队列中的头消息。
//与此同时,若是消息队列处于阻塞状态,则需要唤醒
if (p == null || when == 0 || when < p.when) {
//当上一个事件被阻塞,又来了一个新的Message,会唤醒消息队列
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {//当前传入信息的处理时间大于等于当前头信息的处理时间的情况。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//for循环方式,将当前传入的Message插入到消息队列中的合适位置
for (;;) {
prev = p;
p = p.next;
//找到比当前传入的消息的处理时间大的Message
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将传入的message插入到链表中合适位置
msg.next = p;
prev.next = msg;
}
//若是需要唤醒,则调用Native层的唤醒,不在阻塞状态
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
总结:一个消息插入到消息队列中,若是插入到合适的中间位置,则不需要唤醒。若是插入到新的头部,与此同时,消息队列处于阻塞状态,则需要唤醒。
接下来,查看一下Looper.looper()
方法中调用到的next()
:
Message next() {
//Native层的引用已经断开,消息队列已经停止,则返回null。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//用于标示第一次遍历,标示为-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
//线程先前处于睡眠状态,重新刷新那些等待处理的Binder进程间通讯请求,避免它们长时间得不到处理。
Binder.flushPendingCommands();
}
//检查当前线程的消息队列中是否有新消息需要处理,若是nextPollTimeoutMillis为-1,则该线程会进入睡眠等待状态
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) {
// 循环方式,获取到下一个异步信息,异步消息不需要被同步阻塞
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 {
//阻塞标记为false
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 {
// 没有更多信息,线程会进入睡眠等待状态,直到被其他线程唤醒。
nextPollTimeoutMillis = -1;
}
// 消息队列已经停止,调用Native层销毁方法
if (mQuitting) {
dispose();
return null;
}
//获取到空闲消息的监听器个数,当第一次遍历,且消息队列中没有消息或者处于阻塞状态,则进行空闲信息的回调操作
if (pendingIdleHandlerCount <0&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//若是没有空闲消息的监听器或者本次next()方法已经发送过一次空闲消息,则不走以下逻辑操作
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
//回调空闲消息的监听器的queueIdle()
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {//不在保持监听状态,则移除掉
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置状态,每次Next()最多发出一个空闲信息
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
总结:MessageQueue的next()一次调用,最多只会发送一个空闲消息,触发空闲消息的监听器的queueIdle()
。
空闲消息的监听器IdleHandler的添加与移除方法:
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
上一篇: hibernate joined-subclass基础(转)
下一篇: MFC消息映射机制
推荐阅读
-
Swoft源码之Swoole和Swoft的分析
-
Qt事件分发机制源码分析之QApplication对象构建过程
-
Netty源码分析之ChannelPipeline(二)—ChannelHandler的添加与删除
-
九、Spring之BeanFactory源码分析(一)
-
Spring源码分析之IoC容器初始化
-
Android数据持久化之File机制分析
-
ThinkPHP6源码分析之应用初始化
-
SQLite3源码学习之test_vfs的共享内存机制讲解
-
并发编程(五)——AbstractQueuedSynchronizer 之 ReentrantLock源码分析
-
Android8.1 SystemUI源码分析之 电池时钟刷新