Android UI性能优化

屏幕渲染机制

  • cpu(gpu sf暂统称cpu)经过各种运算,将数据写入一块内存中,这块内存叫做「帧缓冲」。
  • 帧缓冲可以理解为一个M*N矩阵,数据从上到下一行一行保存。
  • 屏幕在显示的时候,从上到下逐行扫描,依次显示在屏幕上,我们把这样的一屏数据叫做「一帧」。
  • 当一帧数据渲染完后,就开始新一轮扫描,如果CPU「正好」(不正好后面再说)也把下一帧数据写入帧缓冲,那么就会显示下一帧画面,如此循环,我们就看到了不断变化的画面,也就是图像。

过程很简单,执行起来却很难,因为这里有两个问题,撕裂卡顿,为什么?

  • 如果当屏幕渲染到一半的时候,cpu已经整理好了下一帧的数据,给到帧缓冲,这时屏幕继续从帧缓冲中一半的位置开始读取数据,这时画面就会显示异常,这种两个图像重合的的问题叫作图像撕裂
  • 如果当屏幕渲染完一帧画面,cpu还没整理好下一帧数据给到帧缓冲,这时屏幕继续从帧缓冲中读取数据渲染,显示的还是上一帧的数据,这时用户就会感觉到卡顿

那你就会有个疑问了

  • 这cpu怎么这么笨,屏幕都没把数据渲染完,干嘛把数据换了?
  • 屏幕怎么这么傻,数据都被换了,怎么不从头读取呢?

这是因为"现代计算机之父"冯·诺依曼提出了计算机的体系结构: 计算机由运算器,存储器,控制器,输入设备和输出设备构成,每部分各司其职。

  • 屏幕的作用就是渲染,不管外界怎么变化,它就是一行一行的渲染。
  • cpu的任务就是运算,运算好了把数据丢给帧缓冲,继续运算。

那能不能让cpu停下来?
当然可以,但是不划算,因为这样就等于把cpu的长处给扼杀了。

等等,有个问题,你说当两个图像重合的的问题叫作撕裂,那不是从第二帧开始,时时刻刻都在撕裂?

  • 好吧,我说错了,有一点忘记说了,应该有个时间,在这个时间内渲染完一帧,画面看起来就是连续的,这个时间大约是16ms。

为什么是16ms?

  • 这是因为人眼与大脑之间的协作无法感知超过60fps的画面更新。12fps大概类似手动快速翻动书籍的帧率,这明显是可以感知到不够顺滑的。24fps使得人眼感知的是连续线性的运动,这其实是归功于运动模糊的效果。24fps是电影胶圈通常使用的帧率,因为这个帧率已经足够支撑大部分电影画面需要表达的内容,同时能够最大的减少费用支出。但是低于30fps是无法顺畅表现绚丽的画面内容的,此时就需要用到60fps来达到想要的效果,当然超过60fps是没有必要的。

fps是啥玩意儿?这里说两个概念

  • 屏幕刷新率(Hz):屏幕在一秒内刷新的次数,Android手机一般都是60Hz,也就是一秒刷新60次,当然也有高刷的,但是60Hz足矣。

  • 帧速率(FPS):cpu在一秒内合成的帧数,比如60FPS,就是60 frame per sconds,意思就是一秒合成60帧。如上所述,当屏幕刷新率大于帧速率的时候,会发生卡顿;屏幕刷新率小于帧速率的时候,会发生撕裂。那么怎么解决这个问题呢?

怎么解决呢?Vsync 和 双缓冲

双缓冲+Vsync

  • 屏幕正在从前缓冲读取第一帧数据并渲染,此时cpu计算完第二帧数据,放在后缓冲,等待VSYNC信号。
  • 屏幕将第一帧数据渲染完毕,发出VSYNC信号,cpu收到VSYNC信号,将后缓冲的第二帧数据复制到前缓冲。
  • 同时屏幕继续绘制第二帧数据,cpu开始计算下一帧数据,循环往复。

牛~~,双缓冲+Vsync解决了撕裂问题,但是并没有解决卡顿的问题,当cpu收到Vsync信号后,如果cpu还没有计算完,也肯定就不会交换前后缓冲的数据,也就是说,屏幕再次读取的还是前缓冲的数据,也就是两次显示了一样的画面,也就是产生了卡顿。
是的,卡顿问题是解决不了的,只能优化。

还能怎么优化?三缓冲 !
首先要清楚下,Android屏幕绘制流程。

  1. 任何一个View都是依附于window的
  2. 一个window对应一个surface
  3. view的measure、layout、draw等均是计算数据,这些是cpu干的事
  4. cpu把这些事干好后,在经过一系列计算将数据转交给gpu
  5. gpu将数据栅格化后,就交给SurfeceFlinger(以下简称SF)
  6. SF将多个surfece数据合并处理后,就放入后缓冲区
  7. 屏幕以固定频率从前缓冲区拿出数据渲染,渲染完毕后发送VSYNC,此时前后缓冲区数据交换,屏幕绘制下一帧。

上述7步是建立在开启硬件加速的情况下的,如果没有硬件加速,就去掉gpu部分,就可以简单理解为cpu直接将数据转交给sf,我们简单整理一下数据的传递流程:
「cpu -> gpu -> display」,而且我们看到,cpu和gpu是排队工作的,它俩和屏幕是并行工作的。好,我们来看发生卡顿(jank)的场景

双缓冲卡顿场景

  • 我们可以将Display那一行看作是前缓冲,将GPU和CPU两行叠加起来看作是后缓冲(因为它俩排队使用),将VSYNC线隔离开的竖行看作一个帧。
  • 我们看到,在第一帧里面,GPU墨迹了半天没搞完,以至于在第二帧里面,Display(屏幕)显示的还是第一帧的A数据,此时就产生了Jank(卡顿),并且在一个vsync信号过来后,cpu什么都没做,因为gpu占着后缓冲(那个绿色的长B块),所以cpu只能再等下一个vsync,在下一个vsync里面,cpu终于拿到了后缓冲的使用权,但是cpu计算时间比较长,导致了gpu时间不够用,数据又没算完,再次发生了卡顿,可以说,这次卡顿直接受到了第一次卡顿的影响。
  • 试想: 如果在第一次卡顿的时候,cpu也能计算数据,那么,第二次卡顿可能就不存在了,因为cpu已经在第一次卡顿的时候把蓝色的A给计算完了,第二次完全可以让gpu独自计算(绿色的A),就不存在因为排队导致的时间不够用了,但是!cpu和gpu共用后缓冲,这就导致它们只能轮流使用后缓冲,怎么解决呢?再加一个后缓冲区,让cpu、gpu各用一块。

我们来看引入三缓冲后的效果:


三缓冲使用效果
  • 我们看到,在第一次jank内,cpu使用了第三块缓冲区,自己计算了C帧的数据,假如此时没有三缓冲,那么cpu就只能再继续等下一个vsync信号,也就是在图中蓝色A块的地方,才能开始计算C帧数据,就又引发下一次卡顿。我们看到,通过引入三缓冲,虽然不能避免卡顿问题,但是却可以大幅优化卡顿问题,尤其是避免连续卡顿。
  • 但是,三缓冲也有缺点,就是耗资源,所以系统并非一直开启三缓冲,要想真正解决问题,还需要在cpu层对数据尽量优化,从而减小cpu和gpu的计算量,比如:View尽量扁平化,少嵌套,少在UI线程做耗时操作等。

源码层面看cpu做了哪些事
请看这篇文章,View的加载流程
从这里我们可以看出,View加载过程中,使用到的耗时操作

  • IO操作(读取布局文件)
  • Xml解析(解析布局文件)
  • 使用了递归(递归查找每一个View)。
  • 反射(使用反射初始化view)

解决IO操作和Xml解析
如果在可以的情况下,不推荐使用Xml来写布局,推荐使用代码直接new。但是这样一来可维护性大大降低。
借用此思想,github上也有一个由掌阅发布的开源库:https://github.com/iReaderAndroid/X2C

  • 为了即保留xml的优点,又解决它带来的性能问题,我们开发了X2C方案。即在编译生成APK期间,将需要翻译的layout翻译生成对应的java文件,这样对于开发人员来说写布局还是写原来的xml,但对于程序来说,运行时加载的是对应的java文件。     我们采用APT(Annotation Processor Tool)+ JavaPoet技术来完成编译期间【注解】->【解注解】->【翻译xml】->【生成java】整个流程的操作。

RecyclerView优化
RecyclerView一些你可能需要知道的优化技术
参考:
Android 性能优化必知必会
性能优化系列总篇
Android性能优化
一篇小短文,带你了解屏幕刷新背后的故事
Android UI性能优化实战 识别绘制中的性能问题
Android UI性能优化 检测应用中的UI卡顿
Google 发布 Android 性能优化典范
Android性能优化

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容