众所周知的Android系统每隔16ms重新绘制一次activity,也就是说你的app必须在16ms内完成屏幕刷新的所有逻辑操作,这样才能达到60帧/s。而用户一般所看到的卡顿是由于Android的渲染性能造成的。 本篇博客将介绍Android的渲染相关知识。
然而有的时候你的程序会出现这样的情况,如果某个绘制操作超过了16秒用了24秒这时候用户看同一张图片花了32秒而不是16s,用户会感到卡顿,这种现象我们叫-丢帧
首先我们要了解android的渲染机制,android 的渲染主要分为两个组件
1.CPU 2.GPU 由这两者共同完成在屏幕上绘制
Activity如何将复杂的UI转换成用户看得懂的图像并绘制到屏幕上? 这是由格栅化操作完成的,所谓的栅格化就是绘制那些Button,Shape,Path,String,Bitmap等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示,说的俗气一点,就是解决那些复杂的XML布局文件和标记语言,使之转化成用户能看懂的图像,但是这不是直接转换的,XML布局文件需要在CPU中首先转换为多边形或者纹理,然后再传递给GPU进行格栅化,对于栅格化,跟OpenGL有关,格栅化是一个特别费时的操作。
简单理解android的渲染过程 CPU在图像绘制之前向GPU输入这些指令这一过程通过OpenGL-ES 也就是说在屏幕绘制UI对象的时候都需要在CPU中转化成多边形再传递GPU进行格栅化操作
cpu将对象转换为多边形耗时 同样上传到GPU也耗时所以我们要减少对象转换次数以及上传数据的次数,幸运的是OpenGL-ES API允许数据上传到GPU进行数据保存,当下一次绘制按钮的时候只要在CPU的存储器里引用它 所以渲染性能的优化就是尽快的上传数据到GPU尽可能长的在不修改数据的条件下保存数据 虽然android系统已经完成的大部分的优化但是还有一个问题造成了性能的影响–>过度绘制(OverDraw)
屏幕上的某个像素点在同一帧的时间内绘制了多次 当设计上追求更华丽的视觉效果的时候,我们就容易陷入采用越来越多的层叠组件来实现这种视觉效果的怪圈。这很容易导致大量的性能问题,为了获得最佳的性能,我们必须尽量减少Overdraw的情况发生。
按照以下步骤打开Show GPU Overrdraw的选项:设置 -> 开发者选项 -> 调试GPU过度绘制 -> 显示GPU过度绘制
蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,
蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。 绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。 淡红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。 深红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。 我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。
下面我们通过一个例子来优化过度绘制,我们使用上面的方法,定位过度绘制的地方
从图上可以看出,按照过渡绘制从好到坏(蓝-绿-粉红-红)来看,文件管理器的过渡绘制是非常严重的,而设置界面的过渡绘制则在可以接受的范围内.下面就以文件管理器为主要分析对象,来看看如何对文件管理器的过渡绘制进行优化.
从文件管理器的图,分析出过渡绘制区域 首先看最上面的ActionBar,对比设置界面的ActionBar就可以知道,整个文件管理器存在一个不透明的背景,导致每次绘制时,都要先绘制这个看不见且不透明的背景.这个背景一般是应用的主题自带的背景,所以GPU过渡绘制显示其位蓝色,这个背景是可以进行优化的. 中间的内容部分,最底层是绿色,说明进行了2x的过渡绘制,去掉第一条我们提的那个全局背景,还有一层背景,也就是1x的过渡绘制,对比setting可知,这个背景色也是可以去掉的. 最容易看出的是这两条,我们先分析和优化这两条,然后再进行其他的优化.
优化过渡绘制区域 在进行位置确认后,我们大概确定了过渡绘制的区域,让我们来使用工具来进行验证和View确认.
打开Monitor(Eclipse和Android Studio中都有快捷打开按钮,即DDMS,右上角选择 Hierarchy View,大概使用如图
其中根节点:PhoneWindos$DecorView是整个视图的根节点,唯一的子节点是ActionBarOverlayLayout,这个Layout包含了ActionBar,应用程序,以及SmartBar.
下面讲述如何从Hierarchy View结合代码分析出需要进行修改的区域
去除默认背景 上面分析过渡绘制区域的第一条,整个window存在一个背景,所以进行了一次重绘,这个背景的重绘是系统级别的,和主题有关,即这个背景是属于ActionBarOverlayLayout的.这种类型的过渡绘制解决也比较方便,在文件管理器的主Activity的onCreate方法中,加入
this.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
就可以将这个看不见的主题背景去掉.下面是去掉主题背景后的效果图:
对比优化前的图可以发现,背景被去掉之后,少了一层过渡绘制. ActionBar上的蓝色已经消失了.中间的内容由绿色变为蓝色
消除子控件背景 上面分析的第二条说”中间的内容部分,最底层是绿色,说明进行了2x的过渡绘制”,现在中间部分变成了蓝色,但是这是一个全局的背景,导致右边的view拉过来之后,还是存在大量的红色和绿色. 继续分析Hierarchy View,找到中间view对应的视图:DragRelativeLayout,查看源码可知,DragRelativeLayout继承自公共控件:SlidingMenu ,SlidingMenu 由CustomViewAbove和CustomViewBehind组成,前者是上面可以左右拉动的那部分,后者是底部不能拉动的那部分(这个从HierarchyView中也可以看出来:如下图所示:
点击CustomViewBehind,查看其所占的区域,就可以发现背景是这个View进行绘制的,打开CustomViewBehind的代码可以发现其构造函数中包含下面的代码:
setBackgroundColor(getResources().getColor(R.color.mz_slidingmenu_background_light));
这个背景是不需要的,查看源码可知,这个view会在SlidingMenu.setMenu的时候,被覆盖掉,还是看不到的.所以这一层view是可以去掉的.下面是去掉一层背景之后的预览图:
可以看到这一层背景去掉之后,过渡绘制减轻了很多.
接着进行分析,可以看到CustomViewAbove也是存在一个过渡绘制的背景的,查看Hierarchy View的CustomViewAbove的子节点,可以看到过渡绘制是由ListView导致的.其id为:FilesList,在代码中找到它,并对他进行分析.在我将PartitionItemLayout中onDraw()函数的setBounds去掉之后,过渡绘制进一步改善了(但是ListItem的View的颜色也比之前要浅了,这一步优化需要根据具体情况进行) 下面是优化后的效果图:
通过调整,已经发现绘制优化了很多
使用Lint优化代码
Lint工具的使用比较简单,根据给出的提示做对应的修改即可.有时候需要工具具体情况来确定是否需要修改. 下图是一个简单地例子.箭头处提示这个Layout或者它的父Layout是不必须的.具体修改方法即去掉FrameLayout,将RelativeLayout提升为根VIew即可.
Lint工具还会针对代码中潜在的不合理或者Bed Code做出修改意见.比较重要的提示包括
1.声明但是没有使用的变量 2.可能会产生的空指针 3.没必要书写的return,continue 4.复杂代码的简化写法 5.for循环的简化写法:foreach 6.无效的判空 7.空if 8.无效或者未使用的import
使用Tracer For OpenGL ES Tracer工具也在Android Device Monitor中.点击右上角的Tracer for OpenGL ES按钮就可以进入(如果没有这个按钮,点击旁边的Open Perspective按钮,从选项中选择Tracer for OpenGL ES即可).初次打开Tracer工具,里面是没有内容的,点击右上角的两个按钮(一个是打开现有的GLTrace文件,另一个是新建GLTrace文件)。点击Trace按钮, 手机会自动启动应用程序并启动对应的Activity,当手机上的内容完全绘制出来之后,就可以点击Stop按钮,生成GlTrace文件.文件会自动打开.
分析GLTrace文件,下图是优化过后的图,对比优化前的图可以发现,优化后不会去绘制默认的背景图和CustomViewBehind的背景图.
这只是一帧的绘制,如果多操作几下生成多个帧的绘制trace文件,会发现这两个背景会被多次的重绘,去掉后不仅会减轻过渡绘制,也会加快GUP的绘制速度.