1.1Controlling Hardware Acceleration
从Android3.0(API Level 11)开始,Android 2D渲染管道能够更好的支持硬件加速。硬件加速执行的所有的绘图操作都是使用GPU在View对象的画布上来进行的。因为启用硬件加速会增加资源的需求,因此这样的应用会占用更多的内存。
硬件加速在target api大于等于14的情况下,是默认开启的,但是我们也可以显示的开启硬件加速。如果应用程序只使用标准的View和Drawable,那么打开全局硬件加速不会导致任何不良的绘制影响。然而,由于硬件加速并不支持所有的2D图形绘制操作,因此对于那些使用定制的View和绘制调用来说,打开全局硬件加速,会造成影响。对于这个问题,通常是对那些不可见的元素进行了异常或错误的像素渲染。为了避免这种问题,Android提供了多个级别的硬件加速操作(开启或者关闭),具体可见控制硬件加速。
你可以控制硬件加速在下面不同的级别
(1).Application
(2).Activity
(2).Window
(2)View
Application level
在Android manifest文件,加入下面的属性tag就可到以控制你的应用硬件加速。
<applicationandroid:hardwareAccelerated="true"...>
Activity level
启用和不启动硬件加速在你的activity级别,可以使用android:hardwareAccelerated属性在元件中.下面的例子就是在application中使用硬件加速,而activity不使用。
<applicationandroid:hardwareAccelerated="true">
<activity.../><activity android:hardwareAccelerated="false"/>
</application>
Window level
如果您需要更细粒度的控制,您可以启用硬件加速对于给定的窗口下面的代码:
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
View level
您可以在运行时禁用硬件加速,像用下面的代码的:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
注意:当前情况,不能在View级别开启硬件加速。
1.2判断一个View对象是否被硬件加速
有些时候,知道当前的View对象(尤其是那些定制的View对象)是否被硬件加速对应用程序来说是非常有用的。如果应用程序做了很多定制的绘图操作,并且不是所有的操作都会被新的渲染管道所支持,那么这种判断就特别有用。
有两种不同的方法来检查应用程序是否被硬件加速了:
View.isHardwareAccelerated():返回值为true,View对象跟一个被硬件加速的窗口绑定;反之,未加速
Canvas.isHardwareAccelerated():返回值为true,Canvas对象被硬件加速;反之,未加速
1.3Android的绘图模式(Android Drawing Models)
当硬件加速启用时,Android框架会采用一个新的绘图模式,这种模式利用显示列表把应用程序渲染在屏幕上。充分理解显示列表,以及它们是如何影响应用程序的,对于理解Android是如何绘制没有硬件加速的View对象也是有益的。下面分别介绍基于软件和硬件加速的绘图模式。
当硬件加速启用时,Android框架会采用一个新的绘图模式,这种模式利用显示列表把应用程序渲染在屏幕上。充分理解显示列表,以及它们是如何影响应用程序的,对于理解Android是如何绘制没有硬件加速的View对象也是有益的。下面分别介绍基于软件和硬件加速的绘图模式。
1.3.1基于软件的绘图模式(Software-based drawing model)
在软件的绘图模式中,View对象是通过以下两个步骤来绘制的:
Invalidate the hierarchy
Draw the hierarchy
无论何时,当应用程序需要更新它的UI部分时,它都会调用View#invalidate()(或者invalidate方法的相关变体)使UI内容改变。失效的消息请求会在View对象层次结构上进行一路传递,以便计算出需要重绘的屏幕区域(脏区)。然后,Android系统就会在View层次结构中绘制所有跟脏区相交的区域。不幸的是,这种绘图模式有两个缺点:
第一个问题,在每个绘图传递中,这种绘图模式都需要很多的代码执行。例如,如果应用程序调用了一个按钮的invalidate()方法,并且该按钮位于另一个View对象的上方,那么即使该View对象没有变化,那么Android系统也要重新绘制这个View对象。第二个问题,这个种绘图模式能够隐藏应用程序中的bug。由于Android系统会重新绘制其它跟脏区相交的View对象,所以即使没有调用View对象上的invalidate()方法,那么View对象内容的改变也可能会导致其它View被重绘。当发生这种情况时,就要依赖另一个被失效的View对象来获取适当的行为。这种行为能够改变每次你对应用程序的修改。因为这个原因,所以为了影响绘图代码,在修改定制View对象的数据和状态时,应该始终调用该定制View对象的invalidate()方法。
注意:在View对象的属性发生变化时,如背景色或TextView对象中的文本等,Android会自动的调用该View对象的invalidate()方法。
1.3.2硬件加速绘图模式(Hardware accelerated drawing model)
这种模式下,Android系统依然会使用invalidate()和draw()来请求屏幕更新并且渲染View,但是实际的绘图操作与基于软件的绘图模式是不同的。它会立即执行绘图命令,Android系统把这些命令记录在内部的显示列表中,这个列表包含了View对象层次结构的绘图代码的输出。另一个优化是:Android系统只需要针对由invalidate()方法调用所标记的View对象的脏区进行记录和更新显示列表。没有失效的View对象能够通过重新发布先前被记录的显示列表来进行简单的重绘工作。这种新的绘图模式包含三个阶段:
Invalidate the hierarchy
Record and update display lists
Draw the display lists
使用这种模式,不能够依赖相交的脏区的View#draw()执行。要确保Android系统记录一个View对象的显示列表,就必须调用invalidate()方法,如果忘记调用该方法,那么在变化发生后,View对象看上去会跟变化之前相同。
使用显示列表对提升动画的性能也是有好处的,因为设置特殊属性,诸如透明度、旋转等属性时,不需要请求目标View对象失效(系统会自动做这件事)。这种优化还适用于带有显示列表的View对象(应用程序被硬件加速时的任意View对象)。例如,假设有一个包含了一个Button对象的ListView对象的LinearLayout布局,那么LinearLayout布局的显示列表如下:
DrawDisplayList(ListView)
DrawDisplayList(Button)
假设现在要改变ListView对象的透明度,那么在调用ListView对象的setAlpha(0.5f)方法时,显示列表就包含了以下处理:
SaveLayerAlpha(0.5)
DrawDisplayList(ListView)
Restore
DrawDisplayList(Button)
这里没有执行复杂的ListView对象的绘图代码。相反,系统只是比较简单的更新了LinearLayout对象的显示列表。在一个没有启用硬件加速的应用程序中,该列表(ListView)和它的父对象都会再次执行绘图代码。
Unsupported Drawing Operations
当硬件加速时,2D渲染管道支持最常用的Canvas绘图操作以及许多较少使用的操作。 支持用于渲染随Android提供的应用程序,默认小部件和布局以及常见的高级视觉效果(如反射和平铺纹理)的所有绘图操作。
下表描述了API级别各种操作的支持级别:
Canvas Scaling
首先构建了硬件加速2D渲染流水线,以支持非标尺绘图,一些绘图操作在较高的尺寸值下显着降低了质量。 这些操作被实现为由GPU变换的规模1.0绘制的纹理。 在API级别<17中,使用这些操作将导致缩放工件随规模而增加。
下表显示了何时更改实施以正确处理大尺度:
注意:“简单”形状是带有不具有PathEffect的Paint的drawRect(),drawCircle(),drawOval(),drawRoundRect()和drawArc()(with useCenter = false)), 包含非默认连接(通过setStrokeJoin()/ setStrokeMiter())。 这些绘制命令的其他实例位于上图中的“复杂”下。
如果您的应用程序受到任何这些缺少的功能或限制的影响,您可以通过调用setLayerType(View.LAYER_TYPE_SOFTWARE,null)来关闭应用程序受影响部分的硬件加速。 这样,您仍然可以利用其他地方的硬件加速。 有关如何在应用程序中启用和禁用不同级别的硬件加速的更多信息,请参阅控制硬件加速。
1.4View Layers
在Android的所有版本中,通过使用View对象的绘图缓冲,或使用Canvas.saveLayer()方法,View都具有渲染到离屏(off-screen)缓冲区的能力。离屏缓冲区或层有多种用途,在呈现复杂的动画或使用组合效果时,能够获得更好的性能。例如,使用Canvas.saveLayer()可以实现淡入淡出的效果,先暂时把一个View对象渲染在一个层中,然后把它和不透明因子合成到屏幕上。
从Android3.0(API Level 11)开始,在如何和什么时候使用View.setLayerType()问题上,Android提供了更多的控制。这个API携带两个参数:一个是层的类型,另一个是可选的Paint对象,这个对象描述层应该如何被合成的。使用这个Paint对象能够进行颜色过滤、特殊的混合模式、或者层的透明度。View对象能够使用以下三种层类型:
LAYER_TYPE_NONE:View对象用普通的方式来渲染,并且不是由屏幕外缓存来返回的。这种类型是默认的行为。LAYER_TYPE_HARDWARE:如果应用程序是硬件加速的,那么该View对象被渲染在硬件的一个硬件纹理(texture)中。如果没有开启硬件加速,那么这种层类型的行为与LAYER_TYPE_SOFTWARE相同。LAYER_TYPE_SOFTWARE:View对象会被呈现在软件的一个位图中。
根据以下目的,选择层类型:
性能:使用硬件层类型,把View渲染到一个硬件纹理中。一旦该View对象被渲染到一个层中,那么它的绘图代码直到调用该View对象的invalidate()方法时才会被执行。对于某些动画,如alpha动画,就能够直接使用该层,这么做对于GPU来说是非常高效的。视觉效果:使用层类型(硬件或软件)和一个Paint对象,能够把一些特殊的视觉处理应用给一个View对象。例如,使用ColorMatrixColorFilter对象绘制一个黑白相间的View对象。兼容性:使用软件层类型会强制把一个View对象渲染在软件中。如果被硬件加速的View对象(例如,如果整个应用程序都被硬件加速)发生渲染问题,那么使用软件层类型来解决硬件渲染管道的限制是一个简单的方法。
当应用程序被硬件加速的时候,硬件层能够传递更快、更平滑的动画。当播放具有复杂的绘图操作的动画时,以每秒60帧的速度播放不总是可能的。但是,可以通过使用硬件层把View对象渲染在硬件纹理中,缓解这种情况。硬件纹理能够被用于动画视图,这样在该View对象呈现动画时,就可以消除View对象所需要的重绘操作。除非该View对象的属性发生变化时(invalidate()方法被调用),它才会被重绘。如果在应用程序运行一个动画,并且没有获得想要的平滑结果,就要考虑在动画View上启用硬件层。
当一个View从硬件层被返回时,通过层方法处理的某些属性会被合成到屏幕上。因为它们不需要让View对象失效和重绘,所以设置这些属性是非常高效的。下面列出了影响层被合成的方式。调用这些属性设置器,会导致失效处理的优化,并且不会对目标View对象进行重绘:
alpha:改变层(layer)的透明度;x,y,translation,translation:改变层的位置;scaleX,scaleY:改变层的尺寸;rotation,rotation,rotationY:改变3D空间中层的方向;pivotX,pivotY:改变层的变换起源。
这些属性是在用ObjectAnimator对象给View对象设置动画时所使用的名称。如果想要访问这些属性,就要调用相应的set或get方法。例如,要修改alpha属性,就要调用setAlpha()方法。下面的代码展示了在3D空间中围绕Y轴旋转View对象的最有效的方法:
view.setLayerType(View.LAYER_TYPE_HARDWARE,null);
ObjectAnimator.ofFloat(view,"rotationY",180).start();
因为硬件层会消耗显示内存,因此强烈推荐只在动画播放期间启用硬件层,并且在动画播放结束后就禁用该硬件层。能够使用动画监听器来完成这种操作:
View.setLayerType(View.LAYER_TYPE_HARDWARE,null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"rotationY",180);
animator.addListener(newAnimatorListenerAdapter() {
@Override
publicvoidonAnimationEnd(Animator animation) {
view.setLayerType(View.LAYER_TYPE_NONE,null);
}
});
animator.start();
更多的属性动画的信息,请看Property Animation.
选择硬件加速的2D图形能够有效改善性能,但是为了有效的使用GPU,应该按照以下建议设计应用程序:
系统绘制越多的View对象,就会越慢。这种情况也适用于软件渲染管道。减少View对象的有效方法之一就是优化UI。
在彼此的顶部不要绘制太多的层。移除那些完全被别的不透明View遮盖的View。对于当前硬件的一个好的原则是,每帧的像素数不要大于屏幕上像素数的2.5倍(以位图的透明点阵数来计算)。
一个常见的错误是每次调用渲染方法时创建一个新的Paint对象或Path对象。这样就会强制频繁的运行垃圾回收,导致绕过硬件管道中的缓存和优化。
对于复杂的形状,如路径和圆,是使用纹理掩码来呈现的。每次创建或修改路径,硬件通道都要创建一个新的纹理遮罩,这样会消耗大量的资源。
每次改变位图内容,它都会被再次上传到GPU的纹理,以供下次绘制。
当使用setAlpha()、 AlphaAnimation或ObjectAnimator,让一个View对象半透明时,需要双倍填充率来渲染到离屏缓存。当在一个大的View对象上应用透明效果时,要考虑把View对象的层类型设置为LAYER_TYPE_HARDWARE。
官方地址:http://developer.android.com/guide/topics/graphics/hardware-accel.html