从名字上就可以看得出来,既然叫schedule, 那么Traversals过程肯定是异步调用的,让我们翻翻源码看一下。这里有两个过程比较重要,一个调用了 postSyncBarrier
,另一个调用了 postCallback
,后面会分析这两者分别做了什么。
别忘了我们来这里的目的,是为了看看 doTraversals
是怎么被调用到的,其实 doTraversals
过程被包裹在了mTraversalRunnable之中。
可以看出最后的 doTraversal
是交由Choreographer回调完成的。所以我们这接下来的任务就是分别分析 postSyncBarrier
与 postCallback
了。
postSyncBarrier与postCallback
postSyncBarrier
这里就涉及到Handler同步屏障机制了,同步屏障机制的目的很明确就是为了让某些特殊信息可以更快的得到相应,此处的目的就是让绘制消息优先级超过主线程中其他消息,从而避免绘制不及时导致的用户界面卡顿。
每个线程Looper中的MessageQueue在获取下一个Message的时候,如果碰到了同步屏障,那么不会取出这个同步屏障,而是会遍历后续的Message,找到第一个异步消息取出并返回。这里跳过了所有的同步消息,直接执行异步消息。所以同步屏障机制就像它的名字一样,可以屏蔽同步消息,让异步消息得以快速执行。
值得注意的是同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步小心无法被处理。所以对应到代码中是什么时候移除同步屏障的呢?
可以看出当doTraversals
被Choreographer回调时会移除之前添加的同步屏障。有关同步屏障机制就讲这么多,这里就不着重分析MessageQueue源码了,因为这已经偏离了这篇文章的主题。
感兴趣的话可以拓展阅读 关于Handler同步屏障你可能不知道的问题。
那么问题来了,doTraversal
何时才会被Choreographer编舞者回调呢?我们需要继续分析postCallback了。
postCallback
postCallback
会走到 postCallbackDelayedInternal
,传入的runnable也会被存储到这个mCallbackQueues之中。由于传入的delayMillis为零,所以此时dueTime等于now会走第一个逻辑分支调用 sheduleFrameLocked
。
一路最终会走到 scheduleVsyncLocked
,这里为什么 isRunningOnLooperThreadLocked
返回true,只因其内部判断当前线程Looper是不是构建Choreographer时传入的Looper,由于都是在主线程所以必然返回true。我们继续跟下去。
跟到这里我们看到这个native前缀就不用继续跟下去了,因为其具体实现已经写到了native层,通过JNI进行的方法调用。那么这个Vsync到底是什么东西呢?
屏幕刷新与Vsync脉冲
由于本人并没有深入native分析具体底层实现,所以只能谈谈自己对于Vsync脉冲的理解,如有不对希望大家能够指出。目前Android手机的主流显示屏幕有LCD也有OLED,虽然不同屏幕的次像素排列以及工作过程都不同,所有硬件实现细节都被抽象成HAL层接口,并由ART层SurfaceFlinger进行封装,但是这些底层实现原理都是不需要我们关注的。屏幕刷新率是近年来比较热门的话题,很多厂商通过插帧技术甚至能够实现120赫兹的高刷新率。那么屏幕刷新率是如何体现到SDK中的呢?这里就涉及到了Vsync脉冲。对于60赫兹刷新率的手机,每 16ms(1s / 60 = 0.016666667 )屏幕硬件会发送一个Vsync脉冲通知界面进行刷新。所以可以看出的是,Vsync脉冲频率越高,若存在UI绘制任务则渲染频率也会越高,直接导致的问题就是更加耗电。
感兴趣的话可以拓展阅读 Android屏幕刷新机制—VSync、Choreographer 全面理解。
那么对于Vsync信号的处理在哪呢?
注意的是前面调用scheduleVsync
的实例类型就是这个FrameDisplayEventReceiver,也就是说这个类主要就是处理Vsync信号的。在 onVsync
回调中发送了一个异步消息到消息队列,由于存在同步屏障机制,此时的callback会优先得到处理,逻辑便会走进 doFrame
了。
可以看出 doFrame
中间接调用了 doCallbacks
方法,还记得我们在 postCallBack
传入的callbackType嘛?那就是CALLBACK_TRAVERSAL。
首先他会从之前的mCallbackQueues取出所有CALLBACK_TRAVERSAL类型的callback,然后分别执行,执行后将所有callback进行销毁,避免重复执行,此时doTraversals就会被回调了。不信?动态调试给你看。
这证明了他们之间的关系。
同步屏障的存在可能引发IdleHandler任务不执行
有两种场景可能会触发这个问题。
-
自定义View在重写的onDraw中直接或间接调用invalidate()。
-
重写onMeasure、layout中直接或间接调用requestLayout()。
第一种场景出现的可能性高一些,但是为什么会出现这种情况呢?
IdleHandler是什么
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后
都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。
技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;
我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
一个方向参考。
2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。