1.Android 性能优化之渲染篇
1.VSYNC
帧率: GPU 在 1 秒内绘制操作的帧数. 如 60fps.
我们通常都会提到 60fps 与 16ms, 这是因为人眼与大脑之间的协作无法感知超过 60fps 的画面更新.
开发 app 的性能目标就是保持 60fps, 这意味着每一帧只有 16ms=1000/60 的时间来处理所有的任务
刷新率: 屏幕在 1 秒内刷新屏幕的次数. 如 60Hz, 每 16ms 刷新 1 次屏幕.
GPU 获取图形数据进行渲染, 然后屏幕将渲染后的内容展示在屏幕上.
大多数手机屏幕的刷新率是 60Hz, 如果 GPU 渲染 1 帧的时间低于 1000/60=16ms, 那么在屏幕刷新时候都有最新帧可显示. 如果 GPU 渲染某 1 帧 f 的时间超过 16ms, 在屏幕刷新时候, f 并没有被 GPU 渲染完成则无法展示, 屏幕只能继续展示 f 的上 1 帧的内容. 这就是掉帧, 造成了 UI 界面的卡顿.
下面展示了帧率正常和帧率低于刷新率 (掉帧) 的情形
2.GPU 渲染: GPU 渲染依赖 2 个组件: CPU 和 GPU
CPU 负责 Measure,Layout,Record,Execute 操作.
GPU 负责 Rasterization(栅格化)操作.
Resterization 栅格化是绘制那些 Button,Shape,Path,String,Bitmap 等组件最基础的操作. 它把组件拆分到不同的像素上进行显示. 这是一个很费时的操作.
CPU 负责把 UI 组件计算成 Polygons(多边形),Texture(纹理), 然后交给 GPU 进行栅格化渲染.
为了 App 流畅, 我们需要确保在 16ms 内完成所有 CPU 和 GPU 的工作.
3. 过度绘制
Overdraw 过度绘制是指屏幕上的某个像素在同一帧的时间内被绘制了多次. 过度绘制会大量浪费 CPU 及 GPU 资源 / 占用 CPU 和 GPU 的处理时间
过度绘制的原因
UI 布局存在大量重叠
非必须的背景重叠.
如 Activity 有背景, Layout 又有背景, 子 View 又有背景. 仅仅移除非必要背景就可以显著提升性能.
子 View 在 onDraw 中存在重叠部分绘制的情况, 比如 Bitmap 重叠绘制
4. 如何提升渲染性能
移除 XML 布局文件中非必要的 Background
保持布局扁平化, 尽量避免布局嵌套
在自定义 View 的 onDraw 中避免过度绘制.
代码实例:
- public class OverdrawView extends View {
- public OverdrawView(Context context) {
- super(context);
- init();
- }
- public OverdrawView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- init();
- }
- public OverdrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
- private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Bitmap bitmap1,bitmap2,bitmap3;
- private void init(){
- paint.setStyle(Paint.Style.FILL);
- bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.png1);
- bitmap2 = BitmapFactory.decodeResource(getResources(),R.mipmap.png2);
- bitmap3 = BitmapFactory.decodeResource(getResources(),R.mipmap.png3);
- }
- int w,h;
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- w = getMeasuredWidth();
- h = getMeasuredHeight();
- }
- private boolean Overdraw = true;
- @Override
- protected void onDraw(Canvas canvas) {
- if(Overdraw){
- // 默认会出现过度绘制
- canvas.drawBitmap(bitmap1,0,0,paint);
- canvas.drawBitmap(bitmap2,w/3,0,paint);
- canvas.drawBitmap(bitmap3,w*2/3,0,paint);
- }else{
- // 使用 Canvas.clipRect 避免过度绘制
- canvas.save();
- canvas.clipRect(0,0,w/3,h);
- canvas.drawBitmap(bitmap1,0,0,paint);
- canvas.restore();
- canvas.save();
- canvas.clipRect(w/3,0,w*2/3,h);
- canvas.drawBitmap(bitmap2,w/3,0,paint);
- canvas.restore();
- canvas.save();
- canvas.clipRect(w*2/3,0,w,h);
- canvas.drawBitmap(bitmap3,w*2/3,0,paint);
- canvas.restore();
- }
- }
- // 切换是否避免过度绘制
- public void toggleOverdraw(){
- Overdraw = !Overdraw;
- invalidate();
- }
- }
复制代码
效果图:
来源: https://juejin.im/post/5b99e03ae51d450e6237c007