Android 渲染机制深入理解
基础知识
cpu: *处理器,它集成了运算,缓冲,控制等单元,包括绘图功能.cpu将对象处理为多维图形,纹理(bitmaps、drawables等都是一起打包到统一的纹理)。
gpu:一个类似于cpu的专门用来处理graphics的处理器, 作用用来帮助加快格栅化操作,当然,也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。
opengl es:是手持嵌入式设备的3dapi,跨平台的、功能完善的2d和3d图形应用程序接口api,有一套固定渲染管线流程. opengl es详解
displaylist 在android把xml布局文件转换成gpu能够识别并绘制的对象。这个操作是在displaylist的帮助下完成的。displaylist持有所有将要交给gpu绘制到屏幕上的数据信息。
格栅化 是 将图片等矢量资源,转化为一格格像素点的像素图,显示到屏幕上。
垂直同步vsync:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知gpu在载入新帧之前,要等待屏幕绘制完成前一帧。下面的三张图分别是gpu和硬件同步所发生的情况,refresh rate:屏幕一秒内刷新屏幕的次数,由硬件决定,例如60hz.而frame rate:gpu一秒绘制操作的帧数,单位是30fps,正常情况过程图如下:
渲染机制分析渲染流程简介
android整体的绘制流程如下:
ui对象—->cpu处理为多维图形,纹理 —–通过opegl es接口调用gpu—-> gpu对图进行光栅化(frame rate ) —->硬件时钟(refresh rate)—-垂直同步—->投射到屏幕
android系统每隔16ms发出vsync信号(1000ms/60=16.66ms),触发对ui进行渲染, 如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着计算渲染的大多数操作都必须在16ms内完成。
渲染时间线
正常情况下android的gpu会在16ms完成页面的绘制,如果一帧画面渲染时间超过16ms的时候,垂直同步机制会让显示器硬件 等待gpu完成栅格化渲染操作,然后再次绘制界面,这样就会看起来画面停顿。
当gpu渲染速度过慢,就会导致如下情况,某些帧显示的画面内容就会与上一帧的画面相同。
渲染常见问题
gpu过度绘制
overdraw是开发中常见的优化点,是指一个界面出现层层绘制的情况,如:
我们可以使用一些第三方工具来查看是否过渡绘制。如小米魅族。
任何时候view中的绘制内容发生变化时,都会重新执行创建displaylist,渲染displaylist,更新到屏幕上等一 系列操作。这个流程的表现性能取决于你的view的复杂程度,view的状态变化以及渲染管道的执行性能。
当view的大小发生改变,displaylist就会重新创建,然后再渲染,而当view发生位移,则displaylist不会重新创建,而是执行重新渲染的操作。
所以当界面过于复杂的时候,displaylist绘制界面就会出现延迟而造成卡顿。
我们可以使用渲染工具检测,工具中,不同手机呈现方式可能会有差别.分别关于statusbar,navbar,激活的程序activity区域的gpu rending信息。激活的程序activity区域的gpu rending信息。
我们打开手机的gpu rending呈现的信息,我们以魅族为例:
说明:每一条柱状线都包含三部分,
蓝色代表测量绘制display list的时间,
红色代表opengl渲染display list所需要的时间,
黄色代表cpu等待gpu处理的时间。
android渲染优化
读懂android的渲染机制对于优化,特别是在写布局的时候是很有帮助的。减少布局层级,减少gpu的渲染这对我们提供app的质量是很有帮助的。
去掉不必要的界面:
布局层级优化
使用hierarchy viewer工具可以查看界面的层级,关于这块的介绍请查看:android布局优化
图片格式选择
android的界面能用png最好是用png了,因为32位的png颜色过渡平滑且支持透明。jpg是像素化压缩过的图片,质量已经下降了,再拿来做9path的按钮和平铺拉伸的控件必然惨不忍睹,要尽量避免。有条件的可以选择webpp,这种格式的图片占据的大小比较小,并且能满足手机显示的需要。
当背景无法避免,尽量用color.transparent
因为透明色color.transparent是不会被渲染的,他是透明的。
所以我们在设置界面的时候需要做一个判断:
bean bean=list.get(i); if (bean.img == 0) { picasso.with(getcontext()).load(android.r.color.transparent).into(holder.imageview); holder.imageview.setbackgroundcolor(bean.backpic); } else { picasso.with(getcontext()).load(bean.img).into(holder.imageview); holder.imageview.setbackgroundcolor(color.transparent); }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!