一、消息分类
1. 普通消息
日常使用Handler时,默认都是同步消息。
Message message = Message.obtain();
message.what = XXX;
mHandler.sendMessageDelayed(message, 1000);
or
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
// 省略部分代码...
}
});
}
}).start();
2. 异步消息
在创建Handler时,传入的async参数是true。
public Handler(Callback callback, boolean async) {
// 省略部分代码...
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
或者发送的Message设置了setAsynchronous(true)。
Message message = Message.obtain();
message.what = XXX;
message.setAsynchronous(true);
mHandler.sendMessageDelayed(message, 1000);
3. 屏障消息
通过反射的方式调用。特征是target为null,不需要做分发处理。用于拦截队列中msg.when之后的同步消息,放行异步消息。在移除对应msg.arg1的屏障消息后,同步消息可恢复执行。
// 反射插入消息屏障
MessageQueue queue = mHandler.getLooper().getQueue();
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
method.setAccessible(true);
token = (int)method.invoke(queue);
// 反射移除消息屏障
MessageQueue queue = mHandler.getLooper().getQueue();
Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
method.setAccessible(true);
method.invoke(queue, token);
二、源码分析
1. 插入屏障
/**
* @hide 无法直接调用,只能反射调用
*/
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
// 屏障消息的的唯一标识
final int token = mNextBarrierToken++;
// 屏障消息msg介于prev和p之间
final Message msg = Message.obtain();
msg.markInUse();
// 重置msg的when和arg1,arg1的值设置为token,这里没有给target赋值
msg.when = when;
msg.arg1 = token;
// 当前位置的上一条Message
Message prev = null;
// 将消息队列中的第一个Message赋值给p
Message p = mMessages;
if (when != 0) {
// 对比p的时间和屏障的时间,确定屏障消息插入的位置
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
// 屏障消息不在消息队列的头部
msg.next = p;
prev.next = msg;
} else {
// 屏障消息在消息队列的头部
msg.next = p;
mMessages = msg;
}
return token;
}
}
2. 移除屏障
/**
* @hide 无法直接调用,只能反射调用
*/
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
// target为null 或者 token匹配,则p为对应的屏障消息
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
// 从消息队列中删除屏障消息p
if (prev != null) {
prev.next = p.next;
// 如果屏障消息p之前有消息,说明已对之前的消息做过唤醒或休眠处理,不用再次唤醒
needWake = false;
} else {
mMessages = p.next;
// 如果删除在队列头部的屏障消息p之后,队列中没有新消息了,或者接下来的不是屏障消息,则需要唤醒线程
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
3. 处理消息
Message next() {
// 省略部分代码...
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;
// msg.target == null,则msg为屏障消息,否则队列中的异步消息和同步消息无异
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
// 跳过同步消息:当遍历到消息队列末尾,或者msg是异步消息,则退出循环
} 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.
mBlocked = false;
// 删除屏障消息msg
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.
nextPollTimeoutMillis = -1;
}
// 省略部分代码...
}
}
四、实际应用
ViewRootImpl中,优化View绘制流程的执行响应:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 插入屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
}
}
参考: