【Android 性能优化】布局渲染优化 ( 过渡绘制 | 背景设置产生的过度绘制 | Android 系统的渲染优化 | 自定义布局渲染优化 )





一、 背景设置产生的过度绘制



1. 背景设置产生的过度绘制 :


① 组件背景 : 每个组件每设置一次背景 , 该组件的区域就会增加一层绘制 , 如 LinearLayout 线性布局设置背景颜色 , TextView 设置背景颜色 , 都会增加该组件区域内的过渡绘制 ;

② 布局背景 : 布局文件总的背景 , 会增加一次 GPU 绘制 ;

③ 主题背景 : Activity 界面的主题背景 , 会增加一次 GPU 绘制 ;



2. 组件背景设置策略 : 不要随便为组件添加背景 , 添加一次背景 , 就增加一次 GPU 绘制 ;

不要随意给布局中的 UI 组件设置背景 , 能不设置背景的就不设置背景 , 如 ImageView 组件 , 设置一张图片 , 会增加一次绘制 , 如果再给该 ImageView 组件设置背景颜色 , 那么又会增加一次绘制 , 那么该 ImageView 组件肯定过渡绘制了 ;





二、 Android 系统的渲染优化



【Android 性能优化】布局渲染优化 ( CPU 与 GPU 架构分析 | 安卓布局显示流程 | 视觉与帧率分析 | 渲染超时卡顿分析 | 渲染过程与优化 ) 博客中分析了图像渲染的 16 毫秒过程中

  • CPU 渲染
  • CPU 传递数据到 GPU
  • GPU 渲染

是三大耗时操作 , 上述分析的背景过渡绘制 , 是从减少 GPU 渲染时间角度出发 , 降低图像渲染时间 ;


CPU 传递数据给 GPU 非常耗时 ;


下面分析是从 降低 CPU 传递数据到 GPU 时间 角度出发 , 进行的优化 , 这部分优化是由 Android 系统完成的 ;



1. 透明组件数据传递


Android 系统做了如下自动优化操作 , 当组件的背景是透明的 , 那么 CPU 将该组件转为多维向量图片 ( 多边形和纹理组成 ) 时发现该组件是透明的 , 该组件的图像信息就不会传递给 GPU 进行渲染 , 从而减少了 CPU 向 GPU 传递的数据大小 ; 之前讲到过 , CPU 向 GPU 传递数据也是一个非常耗时的操作 , 因此该优化 , 也降低了组件渲染的时间 ;


透明组件摆放处理 : CPU 不传递这些组件到 GPU 中 , 但是在布局中仍然正常摆放 ;



2. GPU 存储机制


1. GPU 存储纹理机制 : GPU 中的显存可以存储纹理资源 , 即多维向量图形资源 , 在渲染时 , 可以直接使用该存储的资源 , 不用每次都让 CPU 传递数据过来 ;


2. CPU 传递主题资源给 GPU 机制 : 传递主题资源是一次性传递 , 主题中的 背景 , 颜色 , 图片 ( Bitmap , Drawable ) 等资源都打包存储在了多维向量图形 ( 多边形 和 纹理 ) 中 , 传递给 GPU 进行渲染 , GPU 每次进行渲染时直接从存储区域取出这些资源 , 进行渲染 , 不再依赖 CPU 实时传递 ;

这种变化较少的资源 , 适合一次性加载 , 应用或界面的主题资源基本不会改变 ;


3. 普通的 UI 组件资源 : 如果是普通的 UI 组件 , 那么就不能只加载一次了 , 需要每次渲染时 , CPU 都要将组件加载到内存 , 并转成的多维向量图形 , 最后传递给 GPU ;



3. Android 7.0 之后的优化机制


Android 7.0 之后的优化机制 :


① 7.0 系统优化前 : Android 7.0 之前调用 UI 组件的 invalidate 方法 , 组件会回调 onLayout , onMeasure 和 onDraw 方法 ;

② 7.0 系统优化后 : Android 7.0 之后调用 UI 组件的 invalidate 方法 , 组件不会回调 onLayout 和 onMeasure 方法 , 只会调用 onDraw 方法 ;

③ 7.0 系统优化后工作机制 : 在 GPU 中缓存 UI 组件对应的多维向量图形 ( 纹理 ) , 当该组件位置或颜色等外观发生变化时 , 就会通知 CPU , 重新进行加载 , 如 onLayout 摆放 , onMeasure 测量 , 并转为多维向量图 ( 纹理 ) , 传递给 GPU 进行渲染 ; 如果没有发生变化 , 调用 invalidate 方法 , 只会在 GPU 中重新渲染 ; 不会重新 摆放 ( onLayout ) 与 测量 ( onMeasure ) ;





三、 自定义布局渲染优化



1. 自定义组件过度绘制问题描述 : 自定义控件 , 在自定义的 onDraw 方法中 , 绘制多张图片 , 如果图片之间产生重叠 , 重叠绘制的部分就出现了过度绘制 ;



2. 自定义组件绘制原则 :


① 两张图片 : 图片 A A 图片 B B ;

在这里插入图片描述

② 图片覆盖 : 当图片 A A 被图片 B B 覆盖时 , 只绘制图片 A A 显示的部分区域 , 图片 A A 被图片 B B 覆盖的部分不再绘制 ;

在这里插入图片描述

③ 图片 A A 只绘制没有被覆盖的部分 : 只在图片 A A 显示的区域绘制图片 A A 的区域 , 如下图黄色框中的区域 ;

在这里插入图片描述



3. 实现上述图片 A A 在 Canvas 画布上绘制部分图片方式 :


① 完整画布 : onDraw 函数中的 Canvas canvas 参数是完整的画布 ;

② 取出图片 A A 绘制部分的 Canvas 画布 : 这部分画布就是上图中 , 被黄色框框起来的画布 , 传入的四个参数是黄色矩形框的左上右下参数 , 注意剪切之前先保存画布 ;

// 剪切画布前 , 先保存画布 , 之后还要恢复回去
canvas.save();
// 剪切画布
canvas.clipRect(left, top, right, bottom);

③ 在剪切后的画布中绘制图片 A A : 在剪切后的画布中 , 绘制图片 A A , 注意绘制完成后 , 恢复画布 ;

// 在剪切后的画布中 , 绘制图片 A
canvas.drawBitmap(...);
// 绘制完毕后 , 恢复画布
canvas.restore();

④ 绘制效果 : 上述代码的绘制效果大概就是绘制了部分图片 A A , 下图中的下面的部分图片 A A 展示 ;

在这里插入图片描述



3. clipRect 函数原型 : 剪切画布 , 获取 Canvas 完整画布的子画布 , 传入左 , 上 , 右 , 下 , 四个值 , 将画布剪切出来 ;

    public boolean clipRect(float left, float top, float right, float bottom) {
        return nClipRect(mNativeCanvasWrapper, left, top, right, bottom,
                Region.Op.INTERSECT.nativeInt);
    }

猜你喜欢

转载自blog.csdn.net/han1202012/article/details/106921066