最近去面试,人家问到了 handler 同步屏障,我就记录下这同步屏障到底有啥作用。同步屏障给 handler 消息机制提供了一种优先级策略,能让异步消息的优先级比同步消息高。那怎么开启同步屏障呢?用
MessageQueue.postSyncBarrie()这个方法,它会在 MessageQueue 里插入一条同步屏障 message,不给 Message 赋值 target 属性,还插到 Message 队列的头部。当然啦,源码里还涉及延迟消息,咱们先不管它。这个 target 等于 null 的特殊 Message 就是同步屏障。MessageQueue 在获取下一个 Message 的时候,如果碰到了同步屏障,就不会取出这个同步屏障,而是会去遍历后面的 Message,找出第一个异步消息取出来返回。这就跳过了所有的同步消息,直接执行异步消息。为啥叫同步屏障呢?因为它能把同步消息屏蔽掉,先执行异步消息。消息最终的处理是在消息轮询器 Looper.loop()里,而 loop()循环中会调用 MessageQueue.next()从消息队列里取消息,来瞅瞅关键代码
同步屏障消息并不会自己移除,需要调用相关代码来移除同步屏障消息ViewRootImpl#unscheduleTraversals()。
在绘制流程里用上同步屏障,能确保在 vsync 信号来的时候,绘制任务能马上执行,免得让界面卡顿。不过这样也有相应的坏处:咱们的同步消息最多可能得延迟一帧的时间,也就是 16ms 呢,这会给执行主线程 Looper 带来太大压力,得等到 VSYNC 信号来了,才集中处理所有消息。要改善这个问题,办法就是:用异步消息。当咱们把异步消息发到 MessageQueue 里时,在等着 VSYNC 的这段时间,也能执行咱们的任务,能让咱们安排的任务更快执行,还能减轻主线程 Looper 的压力。可能有的读者会想,异步消息机制本来就是为了防止界面卡顿,那咱们直接用异步消息,会不会有啥风险?这咱们得琢磨琢磨,啥样的异步消息会让界面卡顿:异步消息任务执行的时间太长、异步消息太多。要是异步消息执行的时间太长,就算是同步任务,也会让界面卡顿,这点估计大家都能明白。其次,如果异步消息大量涌来影响界面绘制,那就算是同步任务,也会导致界面卡顿。为啥呢?因为 MessageQueue 是个链表结构,大量的消息会让遍历速度变慢,也会影响异步消息的执行效率。所以咱们得注意这么一点:别在主线程做那种特别重的任务,不管是异步的还是同步的。