提示: - Handler中的异步消息和同步消息是什么?(我们通过Handler发出的Message只是同步消息,异步消息是系统级别的,以前可以反射调用,Android高版本已经被@Hide,无法反射调用了) - 同步屏障又是什么? - Android页面刷新操作(performTraversals()-执行三大流程)本质是投递到主线程的消息队列中处理,为什么能保证界面刷新操作优先执行(即使有很多其他耗时的操作)?
Activity
[-]2019-04-1:Handler中的异步消息和同步消息是什么?同步屏障又是什么?Android页面刷新操作(performTraversals()-执行三大流程)本质是投递到主线程的消息队列中处理,为什么能保证界面刷新操作优先执行(即使有很多其他耗时的操作)?[/-][+]2019-04-1:简述一下 Android 中 UI 的刷新机制?[/+][-]2019-04-1:简述一下 Android 中 UI 的刷新机制?[/-][+]2019-04-01:简述一下 Android 中 UI 的刷新机制?[/+]FeatherHunter commentedon Apr 2, 2019
界面刷新的本质流程
同步屏障的作用
同步屏障的原理?
979451341 commentedon Apr 3, 2019
楼上讲了 应用层,我就说一下 系统层的,
首先屏幕是 大约16.6ms刷新一次(固定的),当界面需要改变时, CPU开始计算,将计算结果 赋予 GPU 的buffer缓存起来,等待刷新时间的到来,然后根据buffer的数据刷新界面
如果当前界面没有变化,CPU不用计算,也不会给GPU的buffer赋值啥的,这个buffer也就没变化,等到刷新时间的到来,会依旧根据buffer刷新屏幕
结论是:界面改不改变都会刷新界面,只是在于CPU是否计算这点区别
UI刷新卡顿,基本都在于卡在CPU计算这一环节,对于根据GPU 的buffer刷新这一环节,在系统里有很高的优先级,楼上就说了同步屏障就是保护这一优先级的一个手段
risechen commentedon May 1, 2019
(1)简单概括:
Android应用程序把经过测量、布局、绘制后的surface缓存数据,通过SurfaceFlinger把数据渲染到屏幕上,通过Android的刷新机制来刷新数据。即应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过显示刷新机制把数据更新到屏幕
(2)应用层:相当于client,把计算好的图层数据通过共享内存shareclient传递给系统层
通过查看Activity的启动流程可以看出每个activity其实都attach了一个PhoneWindow,而PhoneWindow视图树的根节点是一个ViewRootImpl。而整体绘制其实就是在ViewRootImpl的
performTraversals方法调用后,采用深度优先挨个遍历视图中的子view,通过调用子view的onMesure,onLayout和draw方法,计算出它们的大小、位置。至于为何采用深度优先遍历,是因为子view的位置和大小依赖父view,就像我们使用match_parent属性就是依赖父view的宽高。
这里需要注意的是,draw绘制分为cpu和gpu绘制,cpuz绘制又叫软件绘制,gpu又叫硬件绘制。cpu绘制速度慢,兼容性好,gpu绘制速度快,但是兼容性差,占用内存高(8m以上),在android3.0之后才引入了gpu绘制,但是目前还是有很多Opengl接口不支持硬件加速
(3)系统层:service
Android是通过系统层进程SurfaceFlinger服务来把收到的应用层图层数据渲染到屏幕上
主要工作有:
①响应客户端事件,创建Layer与客户端的surface进行连接
②接收客户端数据及属性,修改layer属性,如颜色、尺寸、透明度
③将创建的layer内容刷新到屏幕上
④维护layer的序列(缓冲),并对layer的最终输出做裁剪计算
(4)应用层和系统层通信:简单说就是应用层绘制到缓冲区,SurfaceFlinger把缓存数据渲染到屏幕,两个进程使用安卓匿名共享内存SharedClient缓存需要显示的数据。
由于应用层和系统层分别是两个不同的进程,需要使用跨进程通信来实现数据传输,在Android显示系统中,使用了内部匿名共享内存ShareClient。每一个应用和SurfaceFlinger都会创建一个SharedClient。每个SharedClient中最多可以创建31个SharedBufferStack(window),也就是是说每个应用理论上最多支持31个window,同时每个SharedBufferStack包含2个(<4.1)缓冲区到3个(>=4.1)缓冲区
(5)显示刷新机制:简单说4.1以前无vsync同步机制,绘制时间分散,很可能绘制任务在,vsync信号后16ms末尾才开始,这样就导致帧率很低,4.0以后采用vsync机制,Choreographer强制vsync执行绘制任务,绘制时间得到保证
Android绘制UI采用双缓冲机制,就是SharedBufferStack中的缓冲区,UI总是先在Back Buffer计算完毕后,再交换到Front Buffer渲染到屏幕上。理想情况下一个刷新在16ms内完成(60fps).
4.1之前是双缓冲,4.1之后是3缓冲Tripple Buffer。并不是缓冲区越多越好,只有绘制占用的cpu和gpu时间片都小的时候3缓冲的性能大概率高于双缓冲。
(6)总结
①影响绘制的根本原因:
<1>绘制任务太重,绘制一帧内容耗时太长;
<2>主线程太忙了,导致vsync信号来的时候还没计算好数据导致丢帧;
(7)开发中优化建议:
①主线程不要做耗时操作,如大量的cursor操作和ams操作,执行复杂算法;
②降低gc几率,图片加载优化,避免创建大量对象;
③io密集型任务放在子线程执行
④cpu密集型任务在子线程中串行执行
MoJieBlog commentedon May 18, 2019
我来说下为什么是16.6毫秒刷新一次UI。经常玩游戏的人肯定知道60fps的时候基本上就感觉不到卡顿。这里的60fps就是每秒60帧。1000/60 = 16.666666...所以当手机刷新频率16.6时,用户就不会感到卡顿
Saturdaycong commentedon Aug 15, 2019
siren4 commentedon Aug 25, 2019
1.界面上的任何一个view的刷新请求最终都是调用ViewRootImpl的scheduleTraversals()来实现的。
2.scheduleTraversals() 会先过滤掉同一帧内的重复调用,确保同一帧内只需要安排一次遍历绘制 View 树的任务.
3.scheduleTraversals() 会往主线程的消息队列中发送一个同步屏障,发完同步屏障后 scheduleTraversals() 将 doTraversal() 封装到 Runnable 里面,然后将这个 Runnable 任务以当前时间戳放进一个待执行的队列里,并且向底层订阅下一个屏幕刷新信号Vsync.
4.当下一个屏幕刷新信号发出时,底层就会回调取出之前放进待执行队列里的任务来执行,也就是ViewRootImpl的doTraversal() 操作。
5.doTraversal()中首先移除同步屏障,再会调用performTraversals() 方法根据当前状态判断是否需要执行performMeasure() 测量、perfromLayout() 布局、performDraw() 绘制流程,在这几个流程中都会去遍历 View 树来刷新需要更新的View。
6.等到下一个Vsync信号到达,将上面计算好的数据渲染到屏幕上,同时如果有必要开始下一帧的数据处理。
nealkafuly commentedon Nov 14, 2019
为什么要问这些题目,对项目的优化有作用吗,艹,别乱加面试题好吗,
nealkafuly commentedon Nov 14, 2019
这些题目一般不该系统代码,app需要知道jb
nealkafuly commentedon Nov 14, 2019
哦哦,原来可以根据系统做了什么,然后就可以避免一些搞耗时的动作,比如布局层数减少,这个题目的目的就在这里,会优化就完事啦,还搞这些,浪费时间。
zhouyueyuedsf commentedon Jun 26, 2020
几个点可以说下:
mlinqirong commentedon Dec 16, 2021
16毫秒1帧 1秒60/帧 刷新一次
AndroidJiang commentedon Mar 13, 2024
当然有作用,你优化项目卡顿时,这个就是必须了解的,如果这都不知道,那请问你是如何定位卡顿问题呢?