Android优化笔记(一)——渲染篇(原理)

显示机制和刷新机制

这里写图片描述
Android的显示系统是一个典型的显示系统,它由CPU,GPU,Display组成,CPU负责计算数据,把计算好数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer里存起来,然后Display负责把buffer里的数据呈现到屏幕上。

对于 Android 而言,第一个步骤:CPU 计算屏幕数据指的也就是 View 树的绘制过程,也就是 Activity 对应的视图树从根布局 DecorView 开始层层遍历每个 View,分别执行测量、布局、绘制三个操作后得到Buffer缓存数据,再通过SurfaceFlinger把数据渲染到屏幕上。

SurfaceFlinger 是一个独立的Service, 它接收所有Window的Surface作为输入,根据ZOrder, 透明度,大小,位置等参数,计算出每个Surface在最终合成图像中的位置,然后交由HWComposer或OpenGL生成最终的显示Buffer, 然后显示到特定的显示设备上。

系统由HwComposer模块启动SurfaceFlinger进程,VSYNC信号是由HWC产生的。Choreographer则是接收VSYNC信号的类,每次接收VSYNC信号后,再通知View执行dispatchDraw方法。

Android4.1引入VSYNC机制,Android系统每隔16ms发出VSYNC信号(vertical synchronization –场扫描同步,场同步,垂直同步),触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms(1000/60=16.67ms)内完成。


卡顿原理

如果你的某个操作花费时间是24ms,系统在得到VSYNC信号的时候就无法进行正常渲染,这样就发生了丢帧现象。那么用户在32ms内看到的会是同一帧画面。如果占用了n个16ms,则说明丢了n帧。


判断标准

关于FPS,FPS获取原理:手机屏幕显示的内容是通过Android系统的SurfaceFLinger类,把当前系统里所有进程需要显示的信息合成一帧,然后提交到屏幕进行显示。FPS判断卡顿是不准确的,系统其它进程也会影响FPS,你的APP绘制完成后FPS理论为0.用FPS来判断APP卡顿是不准确的,实测APP放着不动FPS为1,其他进程也会刷新界面,如状态栏。

Android系统在流畅的情况下绘帧速度是60帧/s(即:16.7ms一帧)。当绘帧间隔超过一定时长,我们就可以说此时掉帧了,也就会造成用户直接感官的卡顿。此模块可以统计一秒内绘帧次数(即:流畅度SM),并对丢帧的原因进行代码定位。

首先Android的帧绘制流程是:CPU主线程图像处理->GPU进行光栅化->显示帧。APP产生掉帧的情况大多是由“CPU主线程图像处理”这一步超负载引起的,所以我们思考如何去监控主线程绘制情况。要检测CPU绘制帧的时间,就必须找到那个调用View.dispatchDraw的类,Choreographer类就是那个接受系统垂直同步信号,在每次接受时同步信号触发View的Input、Animation、Draw等操作。

所以我们可以向Choreographer类中加入自己的Callback来冒充View的Callback(Choreographer.FrameCallback中的doFrame通知我们下一帧的到来,再做简单的计数统计),通过此Callback我们可以获得View绘制的时间、可以统计一秒内帧绘制的能力(后面把此值称作“流畅值SM”,它能直观的代表当前时间段的流畅度)。之所以不用FPS来代表当前流畅度,是因为Android系统默认在前台页面静止时,FPS可能为0,FPS低无法直接代表当前处于卡顿。

可以从两个方向来分析流畅度(SM)数据:
1)单次大卡顿:当两次绘帧间隔大于70ms,相当于丢了4帧以上,建议开发人员对耗时的代码进行异步或拆分。
2)低流畅值区间:流畅值低于40帧/s的区间,导致低流畅值区间出现的原因有两类:“单次大卡顿”“连续小卡顿”,建议开发人员针对不同的场景进行优化。


卡顿原因

1、人为在UI线程中做轻微耗时操作,导致UI线程卡顿;
2、布局Layout过于复杂,无法在16ms内完成渲染;
3、同一时间动画执行的次数过多,导致CPU或GPU负载过重;
4、View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;
5、View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
6、内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
7、冗余资源及逻辑等导致加载和执行缓慢;
8、臭名昭著的ANR;

大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。(Google官方说的)


关于Choreographer

控制外部输入事件处理,动画执行,UI变化,以及提交执行都是在同一个类中做的处理,即是Choreographer。

在Choreographer对象中有四条链表,分别保存着待处理的输入事件,待处理的动画事件,待处理的遍历事件,以及待处理的提交时间。

每次执行的时候,Choreographer会根据当前的时间,只处理事件链表中最后一个事件,当有耗时操作在主线程时,事件不能及时执行,就会出现所谓的“跳帧”,“卡顿”现象。

Choreographer的共有方法postCallback(callbackType, Object)是往事件链表中放事件的方法。而doFrame()是消耗这些事件的方法。


Thanks

http://gt.qq.com/
https://www.jianshu.com/p/307ba8911799

猜你喜欢

转载自blog.csdn.net/u012027644/article/details/80422816