1.屏障消息
屏障消息也叫同步屏障消息,作用是提升异步消息的执行优先级的。
首先假设有这样一种场景,在主线程的MessageQuee里面存在很多个消息在排队等待处理,然后这个时候我们去给主线程发送了一条刷新UI的Message,那么这条消息就需要高优处理。
为了实现这一效果,我们需要做3件事情:
1.1.发送屏障消息
给对应的MessageQueue发送一条屏障消息,以ViewRootImpl的scheduleTraversals方法举例:
//发送同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
这里的mTraversalBarrier是返回回来的一个token标志,在后续使用完屏障消息后,需要用这个token去移除屏障消息。
1.2. 发送一条异步消息
首先我们需要知道的是,我们正常往handler发送的消息都是同步消息,即Message中的变量isAsynchronous是false的,只有当isAsynchronous的值是 true,才表示是一个异步消息。
我们可以通过设置这个属性,来标记消息是否是异步消息:
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
1.3. 移除屏障消息
使用完屏障消息后,需要手动移除,避免不能处理同步消息的情况发生:
//移除同步屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
1.4.屏障消息的运行逻辑
Message next() {
......
int nextPollTimeoutMillis = 0;
for (;;) {
......
// 阻塞,nextPollTimeoutMillis为等待时间,如果为-1则会一直阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
// 下面的代码目的是获取一个可用的消息,如果找到就return,
// 没找到就继续后面我省略的代码(省略了IdHandler的相关代码)
// 获取时间,还是通过uptimeMillis这个方法
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 如果队列头部消息为屏障消息,即“target”为空的消息,则去寻找队列中的异步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 如果队列头部消息不是屏障消息,就会直接处理
// 如果是,就会获取异步消息,获取的到就处理,获取不到就去运行省略的代码
if (msg != null) {
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.
// 一个标记,表示next循环是不是还在被阻塞着
mBlocked = false;
// 移除消息
if (prevMsg != null) {
// 移除异步消息
prevMsg.next = msg.next;
} else {
// 移除同步消息
mMessages = msg.next;
}
msg.next = null;
// 标记为正在使用
msg.markInUse();
return msg;
}
} else {
// No more messages.
// 没有获取到消息,接下来运行下面省略的代码,nextPollTimeoutMillis为“-1”,在循环开始的nativePollOnce方法将会一直阻塞。
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
......// IdleHandler
}// end of synchronized
......// IdleHandler
}
}
在获取下一条执行消息的时候,如果存在target为null的消息,那么就是屏障消息,这个时候会忽略掉队列里面的排在前面的同步消息,优先去任务队列里面找寻isAsynchronous为true的移步消息,并返回。
2.IdleHandler
IdleHandler会在Looper消息处理空闲的时候去执行,消息空闲包括消息队列为空,或者最近需要执行的消息比当前时间晚。
2.1.基本使用方法
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.v(TAG, "#looper message has worked out");
return false;
}
});
}
这里返回false,表示只会执行一次回调;返回true,则每次Looper空闲的时候都会去执行。
2.2.内部管理
在MessageQueue中,有一个集合来管理IdleHandler,用来增加或者删除:
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<>();
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);
}
}
2.3.实现原理
处理IdleHandler的代码,同样是在Message.next()方法里面:
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
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;
// 注释1 屏障消息处理,获取异步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 注释2 获取到Message不为null,则说明存在需要处理的Message
if (msg != null) {
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) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; //正常返回消息
}
} else {
// No more messages.
// MessageQueue无消息,设置延迟为-1,nativePollOnce无限等待,直到有消息
nextPollTimeoutMillis = -1;
}
// 注释3 执行到此处,说明没有需要执行的Message(MessageQueue为空,或者存在延迟Message)
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 注释4 IdleHandler队列为空,不执行,进入下一个循环,此后不再会执行IdleHandler判断,除非下次进入next方法
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)];
}
// 注释5 mIdleHandlers数组赋值给 mPendingIdleHandlers
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 注释6 执行IdleHandler队列中的空闲任务
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 {
// 注释7 执行任务逻辑,并返回任务释放移除
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
// 注释8 移除任务,下次不再执行
mIdleHandlers.remove(idler);
}
}
}
// 注释9 重置IdleHandler的Count为0,避免下次重复执行IdleHandler队列
pendingIdleHandlerCount = 0;
}
}
需要注意的是,IdleHandler内部的执行,也是在当前线程执行的,如果是在UI线程中,同样也不能做太过耗时的处理,以免发生ANR的问题。