MessageQueue是什么结构:
- 是一个单链表
- 链表节点的排序方式以时间(when)来排序
因为个人水平问题,对native方法没有能力进行分析,只能根据链表的添加移除以及查找方面对MessageQueue进行分析。
成员变量
// True if the message queue can be quit.
//如果消息可以退出,则为真
//系统创建的UI线程的MessageQueue是不运行的 其他的都是允许的
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
//nativie方法相关
private long mPtr; // used by native code
//消息队列的头Head
Message mMessages;
//存放IdleHandler的集合
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
//存放IdleHandler的数组
private IdleHandler[] mPendingIdleHandlers;
//当前队列是否处于正在退出的状态
private boolean mQuitting;
//当前列队是否正在等待被激活以获取消息
private boolean mBlocked;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
上面这些成员变量中,需要注意的是mMessages,mIdleHandlers,mPendingIdleHandlers
mQuitting,mBlocked。
构造函数
MessageQueue 的构造函数非常简单
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//创建了NativeMessageQueue对象
nativeInit();
}
接口IdleHandler
/**
*以用来在线程空闲的时候,指定一个操作
*/
public static interface IdleHandler {
/**
* MessageQueue发现当前没有更多消息处理时,可以顺带执行queueIdle里面的方法。
* 如果返回false,调用完毕后会从mIdleHandlers中移除。
* 如果返回true,调用完毕之后还会保留
*/
boolean queueIdle();
}
//添加方法
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);
}
}
举个简单的用法:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
....
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//TODO
return false;
}
});
}
它可以保证在线程空闲状态时才调用该方法,比如页面复杂的情况下,绘制的比较慢,绑定数据就可以放在这里面进行操作。(onResume 中你的行为会在绘制之前执行,页面还是会出现卡顿)
这里有篇干货:你知道 android 的 MessageQueue.IdleHandler 吗?
入队方法enqueueMessage
也就是add方法,它会对加入的message进行排序。
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 {
//进入这里,p!=null when>=p.when
// 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;
// 插入在prev后面,p的前面.时按照时间来排序的
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
msg.target 就是handler,when是执行时间,mMessages是链表的头结点。
经过一系列参数检查,开始入队。
代码逻辑如下:
if的第一个条件如果成立
如果p(此刻p为头结点)==null 或者传入的参数为0,或者when<p.when 就是小于头结点的时间,把传入的msg做为p的前一个节点插入(msg.next=p),同时把msg赋值给mMessages做为头结点。
- 假如目前链表没有任何数据,此时p==null成立,那msg插入进来就直接是头节点了。
- 假如p做为头结点不为null p.when=1000 when=300 此时条件依然成立,msg.next=p,把msg的指针域指向前头结点p,同时把msg赋值给mMessages,成为头节点,而p则成为了第二个节点。
然后可以发现,第三个添加如果满足该条件 依然会插在头结点前面,成为新的头结点。
else 成立
此时,p!=null when>=p.when
死循环开始,从p开始,把p做为prev就是前一个节点,p.next p的下个节点做为当前节点,开始从头到尾进行遍历,如果满足,p==null(遍历完了)或者when<p.when(时间小于p的时间),就退出死循环。
- 进入for循环,有头结点,先把头节点做为上个节点,第二个节点做为当前节点p=p.next,如果p==null说明链表已经遍历完毕,而when>=p.when,所以msg.next=p,prev.next=msg,msg插入在p的后面,变成最后的节点。
- p!=null 但是when<p.when,假设p=p.next可能是第三个节点,而传入的when<p.when,比第三个节点的时间小,跳出死循环。msg.next=p 把msg放在p的前面,prev.next=msg 同时,把msg放在第二个节点的后面。完成按时间顺序的插入排序。
查询方法next
Message next() {
...
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;
//如果消息不为null 并且不是异步的 会一直循环,,,
} while (msg != null && !msg.isAsynchronous());
}
//跳出循环,并找到一条异步消息
if (msg != null) {
//如果当前事件,比msg.when就是handler定时的事件小,
if (now < msg.when) {
//下一条信息还没有准备好。设置一个超时,在它准备好时唤醒。
// 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 {
// Got a message.
//
mBlocked = false;
if (prevMsg != null) {
//把链表的next指向当前的next,就是把它从列队中移除出去
prevMsg.next = msg.next;
} else {
//如果prevMsg 那msg肯定是头结点,把头结点的next 赋值给mMessage头结点。
mMessages = msg.next;
}
//移除当前msg的next指针域的引用
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//如果处于正在退出队列,就是停止处理消息时,调用dispose
//Looper的loop方法,检查到message为null 回退出loop
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) {
// 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方法,是一个读取异步信息,并从队列取出删除的方法。
首先,给pendingIdleHandlerCount=-1,nextPollTimeoutMillis=0;
接着进入无限循环中,如果nextPollTimeoutMillis!=0;调用的Binder的flushPendingCommands()方法,把绑定在当前线程中的命令,刷新到内核中,用来阻塞线程。进入到同步代码块中,获取当前系统启动时间,如果msg不为null,并且handler==null,进入do while循环,如果,msg为null,或者msg是个异步信息,就跳出选好,目的是为了获取第一个异步消息。
- msg不为null,进入下面第一if中,判断如果当前时间比,msg.when的时间小,说明还没到执行时间,设置一个超时时间,等代时间到了再唤醒。如果now>=msg.when到了执行时间,如果prevMsg!=null,通过prevMsg.next = msg.next 移除msg msg.next = null msg.markInUse(); 同时return出去。
- msg为null,表示没有更多要处理的信息,nextPollTimeoutMillis = -1;
如果msg为null,同时mQuitting不为true,则表示队列处比较空闲,此时就会执行到下面的代码,经过一系参数判断,对mPendingIdleHandlers和mIdleHandlers进行处理。
也就是上面提到的IdleHandler的接口回调。
// 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);
}
}
}
移除方法removeMessages
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (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.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
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;
}
}
}
这两个移除方法基本一样,只是参数不一样,逻辑相同。
- 首先先判断handler为不为null,为null直接return。进入同步代码块,两个while循环。
- 第一个while,是删除前面的,就是从头部开始,如果符合条件,就一直更换头部,确保头节点的next不符合条件。
- 第二个while循环,则从不符合条件的第一个节点开始往后删除所有符合条件的节点。
这里有个疑问,不清楚第一个while循环的意义,直接第二个循环删除到底不久OK了。可能是因为头结点要特殊处理???
判断是否有消息hasMessages
boolean hasMessages(Handler h, int what, Object object) {
if (h == null) {
return false;
}
synchronized (this) {
Message p = mMessages;
while (p != null) {
if (p.target == h && p.what == what && (object == null || p.obj == object)) {
return true;
}
p = p.next;
}
return false;
}
}
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;
}
}
这2个方法都是根据message中的字段(target、what、callback、obj)来查找相应的message。唯一要注意的就是如果h为null的话,方法直接返回false。
还有一点要注意的是如果object传null的话,则忽略匹配obj字段。
退出队列方法quit
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
//如果正在出队 返回
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
根据safe的值来选择不同的退出策略。
- 如果是安全退出,执行removeAllFutureMessagesLocked(),逻辑是,如果队头的时间已经超过当前时间(说明其余的成员全都超过了改时间),则直接执行removeAllMessagesLocked()的退出策略。 反之,则进行循环遍历找到第一个p.when>now的结点message(从该message节点之后,所有的元素都必将大于now),先将前一个节点p的指针域指向null,接着do while循环,对该节点之后的元素都进行回收,如果先前的for死循环 遍历到最后一位也没查到符合条件的节点,则直接return。
方法removeAllFutureMessagesLocked()
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
//回收消息
p.recycleUnchecked();
} while (n != null);
}
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
至此,MessageQueue的大部分关键代码都已分析完毕,接下来,我们来分析一下Android消息机制中的Lopper。