关于 Handler 同步屏障你可能不知道的问题,已获万赞
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
代码有点长,重点在于:没有给 Message 赋值 target 属性,且插入到 Message 队列头部。当然源码中还涉及到延迟消息,我们暂时不关心。这个 target==null 的特殊 Message 就是同步屏障
MessageQueue 在获取下一个 Message 的时候,如果碰到了同步屏障,那么不会取出这个同步屏障,而是会遍历后续的 Message,找到第一个异步消息取出并返回。这里跳过了所有的同步消息,直接执行异步消息。为什么叫同步屏障?因为它可以屏蔽掉同步消息,优先执行异步消息。
我们来看看源码是怎么实现的:
Message next() {
···
if (msg != null && msg.target == null) {
// 同步屏障,找到下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
···
}
如果遇到同步屏障,那么会循环遍历整个链表找到标记为异步消息的 Message,即 isAsynchronous 返回 true,其他的消息会直接忽视,那么这样异步消息,就会提前被执行了。
注意,同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步消息无法被处理。我们可以看一下源码:
Message next() {
...
// 阻塞时间
int nextPollTimeoutMillis = 0;
for (;;) {
// 阻塞对应时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 同步屏障,找到下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while
(msg != null && !msg.isAsynchronous());
}
// 如果上面有同步屏障,但却没找到异步消息,
// 那么 msg 会循环到链表尾,也就是 msg==null
if (msg != null) {
···
} else {
// 没有消息,进入阻塞状态
nextPollTimeoutMillis = -1;
}
···
}
}
}
可以看到如果没有即时移除同步屏障,他会一直存在且不会执行同步消息。因此使用完成之后必须即时移除。但我们无需操心这个,后面就知道了。
如何发送异步消息
上面我们了解到了同步屏障的作用,但是会发现postSyncBarrier
方法被标记为@hide
,也就是我们无法调用这个方法。那,讲了这么多有什么用?
咳咳~不要慌,但我们可以发异步消息啊。在系统添加同步屏障的时候,不就可以趁机上车了,是吧。
添加异步消息有两种办法:
使用异步类型的 Handler 发送的全部 Message 都是异步的
给 Message 标志异步
给 Message 标记异步是比较简单的,通过setAsynchronous
方法即可。
Handler 有一系列带 Boolean 类型的参数的构造器,这个参数就是决定是否是异步 Handler:
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
// 这里赋值
mAsynchronous = async;
}
在发送消息的时候就会给 Message 赋值:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// 赋值
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
但是异步类型的 Handler 构造器是标记为 hide,我们无法使用,但在 api28 之后添加了两个重要的方法:
public static Handler createAsync(@NonNull Looper looper) {
if (looper == null) throw new NullPointerException("looper must not be null");
return new Handler(looper, null, true);
}
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
通过这两个 api 就可以创建异步 Handler 了,而异步 Handler 发出来的消息则全是异步的。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
如何正确使用
上面我们似乎漏了一个问题:系统什么时候添加同步屏障?。
异步消息需要同步屏障的辅助,但同步屏障我们无法手动添加,因此了解系统何时添加和删除同步屏障是非常必要的。只有这样,才能更好地运用异步消息这个功能,知道为什么要用和如何用。
了解同步屏障需要简单了解一点屏幕刷新机制的内容。放心,只需要了解一丢丢就可以了。
评论