在Handler中,大致分为3种Message,分别是同步消息,异步消息和同步屏障,他们三者都是Message,只是成员变量有些区别.
Handler默认是同步消息.
1. 同步消息和异步消息
创建handler的时候,指定async异步消息参数, 会在消息进入队列的时候,设置消息类型为异步消息\
public Handler(boolean async) {
this(null, async);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {\
msg.target = this;
//mAsynchronous为false,为同步消息
if (mAsynchronous) {
msg.setAsynchronous(true);\
}
return queue.enqueueMessage(msg, uptimeMillis);
}
2. 同步屏障
区分同步和异步消息的目的主要是因为同步屏障的存在 主要作用:屏蔽设置之后的队列中的同步消息,只会执行之后的异步消息。通常用在对用户响应及时性要求比较高的场景,例如:显示app界面
官方解释:
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.\
MessageQueue遍历处理队列中的消息时,如果遇到同步屏障消息(msg.target=null),则之后的同步消息会被禁止直行,直到解除同步屏障
2.1 设置同步屏障的方法
mHandler.getLooper().getQueue().postSyncBarrier()//新建一个target为空的消息
mHandler.getLooper().getQueue().removeSyncBarrier()删除队列中Message的target为null的消息
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//新建一个target为空的消息
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++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
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;
}
}
删除队列中Message的target为null的消息
@UnsupportedAppUsage
@TestApi
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;
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.");
}
//删除队列中Message的target为null的消息
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
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);
}
}
}
2.2 消息处理策略
同步屏障的逻辑是在MessageQueue进行控制的
@UnsupportedAppUsage
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;
//判断是否是同步屏障消息,如果是,则之后的同步消息不处理
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.3 使用场景
屏幕需要绘图显示页面时,设置同步屏障,把绘图message设置成异步消息,这样
//ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//添加同步屏障,下面分析源码
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(\
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
poseCallback会在内部实现一个异步消息,添加到队列中去
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);\
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
doTraversal的删除同步屏障
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除消息屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}