我们在 Handler线程通信机制:实战、原理、性能优化! 中已经知道了Handler的消息同步机制,在MessageQueue的next方法中,有一段逻辑是处理同步屏障的,我们在本章中将分析同步屏障是什么?原理?以及它在Android中的使用。
在Handler的介绍中,我们已经了解了同步屏障,但当时这块不是重点介绍的内容,我们来复习一下它的实现原理。
Message next() {
……
for (;;) {
……
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());
}
……
}
在MessageQueue的next方法中,获取当前执行的消息时,首先对消息进行了判断,如果当前消息是同步屏障消息,则屏蔽后续所有同步消息的执行,异步消息不受影响。
同步屏障消息对象的target属性是null,也就是没有持有Handler对象的引用。
这里我们回想一下,如果消息通过Handler的sendMessage方法添加到消息队列时,会判断target是否为null,如果是null则会抛出错误,所以同步屏障消息的添加不是通过Handler的常用方法添加的。
既然同步屏障不是通过Handler的sendMessage等方法添加的,那么它又是通过哪个方法进行设置的呢?
我们在MessageQueue中发现是postSyncBarrier方法添加的同步屏障消息。
MessageQueue的postSyncBarrier方法:
@TestApi
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) {
//获取token值
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;
}
}
注意:添加同步屏障,不需要唤醒线程。
同步屏障添加后,并不能自动移除,需要调用系统接口手动移除。
MessageQueue的removeSyncBarrier方法:
public void removeSyncBarrier(int token) {//参数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) {//没有找到则抛出error
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
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);
}
}
}
在Android系统中,当应用进行视图的刷新渲染时,用到了同步消息屏障,这里使用的目的是:优先保障视图的刷新逻辑的执行,避免UI线程执行其他同步任务导致的卡顿等问题。
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
ViewRootImpl的scheduleTraversals方法,调用了MessageQueue的postSyncBarrier方法来添加一个同步消息屏障,然后执行视图渲染相关逻辑。
当系统完成UI视图的渲染刷新后,会调用ViewRootImpl的unscheduleTraversals方法移除同步屏障。
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
本文章我们学习了Android消息循环的同步屏障机制原理,以及Android系统中的使用,我们来简单总结下: