Android性能优化(二)——绘制优化
头条
SEO资讯 2019-02-09 11:30:03 字数:10251

每日推荐

料很足,2017年伊始,你需要尝试的25个Android第三方库

http://mp.weixin.qq.com/s/anrWoYBKgSG3fIppAqVWZg

Android 性能优化具体内容参考鸿洋大神的博文:

http://blog.csdn.net/lmj623565791/article/details/45556391

文章详细内容参考:http://mp.weixin.qq.com/s/Dd2apZ5FfxR34L_vuM2rLg

1 Hierarchy View检查布局层次结构

上文讲到,Android性能优化之布局优化,还有一点没说完,就是利用Heirarchy View 检查布局的层次结构。

如果你是使用AS开发的话,你可以在 AS 工具栏中点击 Tools–>Android–>Android Device Monitor–>Hierarchy View。此图可以清楚的看到布局结构,通过此结构可以看到那些布局是多余的。ContentFrameLayout接点之后就是我们上面XML代码的布局了。从中可以看出 好多布局容器都是可以去掉的。本例子只做演示。

Android性能优化(二)——绘制优化

Android性能优化(二)——绘制优化

2

避免过度绘制

你可以在手机打开 设置—->开发者选项—->显示GPU过度绘制,这个开关的作用是按不同颜色值来显示布局的过度绘制,绘制的层次从最优到最差:蓝,绿,淡红,红。给出一张直观的形象图片来表示吧

Android性能优化(二)——绘制优化

屏幕上不同的颜色代表着什么?

1.原色–没有被过度绘制– 这部分的像素点只在屏幕上绘制了一次。

2.蓝色–1次过度绘制– 这部分的像素点只在屏幕上绘制了两次。

3.绿色–2次过度绘制– 这部分的像素点只在屏幕上绘制了三次。

4.粉色–3次过度绘制– 这部分的像素点只在屏幕上绘制了四次。

5.红色–4次过度绘制– 这部分的像素点只在屏幕上绘制了五次。

怎么解决应用过度绘制?

由上面的知识,我们知道要解决过度绘制。即是要尽量减少屏幕上的红色区域,增加屏幕上的蓝色和绿色区域。我们的目标是要控制界面最多被过度绘制2次(不出现粉色和红色)。

1.合理选择控件容器

既然overdraw是因为重复绘制了同一片区域的像素点,那我们首先想到的是解决布局问题。Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout(这里我们不考虑AbsoluteLayout)。同一个界面我们可以使用不同的容器控件来表达,但是各个容器控件描述界面的复杂度是不一样的。一般来说LinearLayout最易,RelativeLayout较复杂。但是尺有所短,寸有所长,LinearLayout只能用来描述一个方向上连续排列的控件,容易导致布局文件嵌套太深,不符合布局扁平化的设计原理。而RelativeLayout几乎可以用于描述任意复杂度的界面。但是表达能力越强的容器控件,性能往往略低一些,因为RelativeLayout主要在onMeasure和onLayout阶段会耗费更多时间。综上所述:LinearLayout易用,效率高,表达能力有限。RelativeLayout复杂,表达能力强,但是效率稍逊。所以当某一界面在使用LinearLayout并不会比RelativeLayout带来更多的控件数和控件层级时,我们要优先考虑LinearLayout。但是要根据实际情况来做一个取舍,在保证性能的同时尽量避免OverDraw。

2.去掉window的默认背景

当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。去掉window的背景可以在onCreate()中setContentView()之后调用

getWindow().setBackgroundDrawable(null);

或者在theme中添加

android:windowbackground="null";下图显示的是设置前后的区别,明显的少了一层绘制。

3.去掉其他不必要的背景

有时候为了方便会先给Layout设置一个整体的背景,再给子View设置背景,这里也会造成重叠,如果子View宽度mach_parent,可以看到完全覆盖了Layout的一部分,这里就可以通过分别设置背景来减少重绘。再比如如果采用的是selector的背景,将normal状态的color设置为“@android:color/transparent",也同样可以解决问题。这里只简单举两个例子,我们在开发过程中的一些习惯性思维定式会带来不经意的Overdraw,所以开发过程中我们为某个View或者ViewGroup设置背景的时候,先思考下是否真的有必要,或者思考下这个背景能不能分段设置在子View上,而不是图方便直接设置在根View上。

4.ClipRect & QuickReject

为了解决Overdraw的问题,Android系统会通过避免绘制那些完全不可见的组件来尽量减少消耗。但是不幸的是,对于那些过于复杂的自定义的View(通常重写了onDraw方法),Android系统无法检测在onDraw里面具体会执行什么操作,系统无法监控并自动优化,也就无法避免Overdraw了。但是我们可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。除了clipRect方法之外,我们还可以使用canvas.quickreject()来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。

5.使用ViewStub占位

ViewStub是个什么东西?一句话总结:高效占位符。我们经常会遇到这样的情况,运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。推荐的做法是使用android.view.ViewStub,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还是不要显示某个布局。

6.用Merge减少布局深度

Merge标签有什么用呢?简单粗暴点回答:干掉一个view层级。Merge的作用很明显,但是也有一些使用条件的限制。有两种情况下我们可以使用Merge标签来做容器控件。第一种子视图不需要指定任何针对父视图的布局属性,就是说父容器仅仅是个容器,子视图只需要直接添加到父视图上用于显示就行。另外一种是假如需要在LinearLayout里面嵌入一个布局(或者视图),而恰恰这个布局(或者视图)的根节点也是LinearLayout,这样就多了一层没有用的嵌套,无疑这样只会拖慢程序速度。而这个时候如果我们使用merge根标签就可以避免那样的问题。另外Merge只能作为XML布局的根标签使用,当Inflate以开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。

7.善用draw9patch

给ImageView加一个边框,你肯定遇到过这种需求,通常在ImageView后面设置一张背景图,露出边框便完美解决问题,此时这个ImageView,设置了两层drawable,底下一层仅仅是为了作为图片的边框而已。但是两层drawable的重叠区域去绘制了两次,导致overdraw。优化方案: 将背景drawable制作成draw9patch,并且将和前景重叠的部分设置为透明。由于Android的2D渲染器会优化draw9patch中的透明区域,从而优化了这次overdraw。 但是背景图片必须制作成draw9patch才行,因为Android 2D渲染器只对draw9patch有这个优化,否则,一张普通的Png,就算你把中间的部分设置成透明,也不会减少这次overdraw。

8.慎用Alpha

假如对一个View做Alpha转化,需要先将View绘制出来,然后做Alpha转化,最后将转换后的效果绘制在界面上。通俗点说,做Alpha转化就需要对当前View绘制两遍,可想而知,绘制效率会大打折扣,耗时会翻倍,所以Alpha还是慎用。如果一定做Alpha转化的话,可以采用缓存的方式。

view.setLayerType(LAYER_TYPE_HARDWARE);

doSmoeThing();

view.setLayerType(LAYER_TYPE_NONE);

通过setLayerType方式可以将当前界面缓存在GPU中,这样不需要每次绘制原始界面,但是GPU内存是相当宝贵的,所以用完要马上释放掉。

3 绘制优化-Android渲染机制

绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个个方面。

首先,onDraw中不要创建新的局部对象,这是因为onDraw方法可能会被频繁调用,这样就会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁GC,降低了程序的执行效率。

另外一方面,onDraw方法中不要做耗时的任务,也不能执行成千上万次的循环操作,尽管每次循环都很轻量级,但是大量的循环仍然十分抢占cpu的时间片,这会造成了View的绘制过程不流畅。按照Google官方给出的性能优化典范中的标准,View的绘制帧率保证60fps是最佳的,这就要求每帧的绘制时间不超过16ms(16ms一1000/60),虽然程序很难保证16ms这个时间,但是尽量降低onDraw方法的复杂度总是切实有效的。

首先了解一下Android 渲染机制:具体内容参考鸿洋大神的博文:

http://blog.csdn.net/lmj623565791/article/details/45556391

文章详细内容参考:http://mp.weixin.qq.com/s/Dd2apZ5FfxR34L_vuM2rLg

0人参与回答
  • 未添加任何数据~~

提示信息

联系电话:
1585150508*
QQ交谈:
小编
站长微信:
站长邮箱: service@qingjiuzhubei.com