您的位置:首页 > 移动开发

APP性能优化之过度绘制【项目实践】

2017-10-30 10:37 537 查看

1.写在前面

最近公司的项目版本刚更新所以有点时间,估计接下来不是对APP进行性能优化就是代码重构。刚好一直对过度绘制非常感兴趣,而且弄完才发现,对于过度绘制我们越早重视后面需要做的事情就越少。

对公司项目的过度绘制问题处理进行处理后,虽然好像肉眼或者感觉体验不出是否优化了(^_^!!),但是凭借工具可以知道确实已经优化了。

2.干货

A.概念

我在之前一直对过度绘制了解太片面了,以为多个布局不断嵌套,然后每个布局都设置自己的background这就算过度绘制,当然这确实算是,但是不仅仅这样,先看看官方给的定义。

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。

举个例子:我们新建一个android studio项目,xml布局和界面不作改动差不多是这样

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>

</RelativeLayout>




我们知道android手机的界面屏幕的刷新频率是60hz,然后换算下单位一秒是1000ms,所以1000/60=16.67ms,所以这16.67ms就是一帧,如果一个view,我们没有在一帧刷新出来就会有丢帧现象,丢帧越多我们就使用就有有卡顿感觉。现在我们需要在一帧的任务中,把上面图片红色框(多个像素组成)刷新出来,我们知道这个框中的内容包含两部分,一部分是文字、一部分是背景,如果在上一帧任务只是完成了一半,那么剩下的一般就会留到下一帧,下一帧还完不成就会一直把剩下的留给下一帧,一直这样下去人的肉眼就可以感到卡顿了,这应该就是过度绘制。所以我们需要尽量减少过度绘制,并不是避免。

B.工具

过度绘制不严重,人很难凭感觉感觉出来,还好android手机提供了工具测出来,进入手机开发者选项,把GPU过度绘制打勾,就可以看到UI界面过度绘制的情况,Google将不同的颜色区分过度绘制的不同情况,具体图片如下



蓝色 过度绘制一次(说明这块像素区域绘制了两次)

绿色 过度绘制两次

粉色 过度绘制三次

红色 过度绘制四次

所以我们的目标就有了,尽量减少鲜艳的颜色在UI上,但是我觉得这样进入开启和关闭跳转界面太多了,所以我采用了大佬开发的一个开发工具APP,牛逼的大佬Trinea ,安装好后一键开关overdraw。

C.实践

调试工具APP界面如下



点击箭头就可以开启了,先用大佬的APP试下,看下他做的APP过度绘制情况怎么样(哈哈~),开启后界面如下



果然是大佬,这处理的简直牛B,没有红色粉色,大部分都是蓝色,只有一点点绿色,说明大部分都只是过度绘制一次,在即兼顾UI界面的美化,又照顾过度绘制,这应该是极限了。不信,拿刚才AS新建的项目试一下,(只是将textview居中,不影响其他)情况如下



这一块像素区域绘制了那两次我们也很容易知道,window本身有个background绘制了一次,然后文字绘制一次,window的background在我们为这个app设置style时就有了

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>


可以点击进去Theme.AppCompat.Light.DarkActionBar看看,一直点击进去后可以找到这个属性



我们可以移除window默认设置的这一层背景,xml和java都可以控制

<item name="android:windowBackground">@null</item>
xxxxxxxxxx分割线xxxxxxxxxxx
getWindow().setBackgroundDrawable(null);


在这里就可以知道通过上面的设置可以减少一次绘制,因为as默认设置的这一个background对我们项目中没什么实际意义,因为项目中的UI界面,我们都定制设置了自定义的颜色。去style中加上上面的代码试一试什么情况(这里为了看清楚,我将字体的颜色设置成白色,这里并不影响绘制,因为反正默认是设置黑色的)



这样出来的UI界面应该不存在过度绘制,但是明显不是我们想要的。所以假如UI要求我们背景是白色,那我们将xml中最外层的背景颜色设置成白色,过度绘制情况如下



虽然最后结果还是过度绘制一次,但是至少这个颜色是我们自己定义的,只是恰好我也设置成白色而已,如果UI需要蓝色,那么没有去除window,那就过度绘制两次了。

D.项目实践

用工具对我项目测试了下,大部分没问题就是这个列表有很大的问题



UI界面粉红色居多,大部分都是绿色,我希望达到的目标去除粉红色,可以有绿色,大部分是蓝色。最后是这样



图中白色表示没有过度绘制

蓝色 一次

绿色 2次

当然我这算是不择手段 的优化了,已经不符合UI给我的设计图了(后面提到)

首先我这个页面和item的布局大致是如下(主要关注background)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/gray"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/recy"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</android.support.v7.widget.RecyclerView>

</RelativeLayout>


下面的item,删掉很多代码,只关注background

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:background="@color/white"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

</RelativeLayout>


首先当然将window的背景设置成null。第一个此页面最大的布局我加了一个灰色的背景色

android:background="@color/gray"


因为UI设计图上每个item之间是没有分割线的,但是如果只有一两个item时,屏幕没满,导致最后一个item显示在屏幕上很难看,所以设计图上添加了个灰色的背景色,如果item没满屏幕时item背景白色,没满的地方显示灰色。所以当把window设置成null时,项目中其他很多地方变成黑色,这里没变化,就是因为这个gray背景色。但是如果这样,那我还需要将item中最大的背景色设置成白色,不然如果不设置,则item背景色也变成灰色gray了。这样布局的代码就变成如上了。

这样对于没有优化之前的过度绘制的颜色就很容易理解了。item中大面积的绿色是过度绘制两次,一共绘制三次,包含:window、总background的gray、item的white,图片和大部分文字粉色就在前面基础上还加了自己本身所以是过度绘制三次。



2. 如果不考虑上面设计的美观问题(只有一两条数据),那接下来就很容,直接将总background的gray改成白色,然后将item中的background去掉。那就变成上面第二张图了,白色的就是只绘制了一次,蓝色过度绘制一次,绿色过度绘制两次。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: