写点什么

关于 Handler 同步屏障你可能不知道的问题,已获万赞

用户头像
Android架构
关注
发布于: 刚刚

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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


(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;


}


}


如何正确使用




上面我们似乎漏了一个问题:系统什么时候添加同步屏障?


异步消息需要同步屏障的辅助,但同步屏障我们无法手动添加,因此了解系统何时添加和删除同步屏障是非常必要的。只有这样,才能更好地运用异步消息这个功能,知道为什么要用和如何用


了解同步屏障需要简单了解一点屏幕刷新机制的内容。放心,只需要了解一丢丢就可以了。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
关于Handler同步屏障你可能不知道的问题,已获万赞