Android-硬件加速

转载请注明来源:http://blog.csdn.net/goldenfish1919/article/details/36890475

从3.0(API level 11)開始。Android 2D渲染pipeline開始支持硬件加速,这意味着全部的绘制操作都是由使用了GPU的canvas来完毕的。

可是启用硬件加速会添加app所须要的资源。app会消耗很多其它的RAM。



假设你的Target API level是>=14的,那么默认是启用硬件加速度的。可是。你也能够明白的手动来启用。
假设你的app仅仅是使用了标准的view和Drawable,开启硬件加速不会带来不论什么不好的绘制效果。
可是,由于并非全部的2D绘制都支持硬件加速,打开硬件加速可能会影响一些自己定义的view或者是绘制调用。常常出现的问题有元素不可见、异常、错误渲染的像素。
为了修正这些问题。Android提供了一些选项用来在不同级别上启用或者是禁用硬件加速。
參考“控制硬件加速”章节

假设你的app要自己定义绘制,你要在实际的硬件设备上打开硬件加速来測试下。然后找到可能存在的问题。


“不支持的绘制操作”这个章节描写叙述了硬件加速存在的已知的问题和怎样来使用它。

控制硬件加速

你能够在下列级别上控制硬件加速:

Application
Activity
Window
View

Application级别

在你的manifest文件里,设置<application>标签的例如以下属性给整个app启用硬件加速:
<application android:hardwareAccelerated="true" ...>

Activity级别

假设全局启用了硬件加速的情况下,你的应用无法正常工作,你也能够控制单个activity是否启用硬件加速。
在activity级别启用或者禁用硬件加速,能够给<activity>加入android:hardwareAccelerated属性。
以下的样例在整个app级别启用了硬件加速,可是在某个activity上禁用了硬件加速。


<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window级别

假设你须要更细粒度的控制。你能够用以下的代码给指定的window启用硬件加速:
getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意: 当前在window级别不能禁用硬件加速。

View级别

不能够用以下的代码在运行时设置单个view禁用硬件加速:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

注意:当前不能在view级别启用硬件加速。View的layer不仅能够禁用硬件加速,有其它的用途。
很多其它详细的使用信息请參考“View的layer”章节。



view是否被硬件加速了

有时候,知道app是否被硬件加速了是非常实用的,尤其是对一些自己定义的view来说。
假设你的app做了非常多自己定义的绘制而且新的渲染pipeline有可能并不支持全部的绘制操作的时候会更实用。

有两种检查app是否被硬件加速的方式:

View.isHardwareAccelerated()返回true,假设view被attached到了硬件加速的window上。
Canvas.isHardwareAccelerated()返回true。假设Canvas是被硬件加速的。

假设你必须要在你的绘制代码中做检查的话。尽可能的使用Canvas.isHardwareAccelerated()而不是View.isHardwareAccelerated()。
当view被attached到一个硬件加速的window上,它仍然能够使用非硬件加速的Canvas来做绘制。


比方说,当为了缓存而把view绘制到bitmap的时候。就会发生这样的情况。

Android的画图模型

当启用了硬件加速的时候,Android框架会使用一个新的画图模型。它会使用显示列表(display list)来把app渲染到屏幕上。
为了彻底的理解显示列表和显示列表是怎样影响你的app的,首先要明白在没有硬件加速的时候,android是怎样绘制view的。


以下的章节介绍了基于软件的和基于硬件加速的画图模型。



基于软件的画图模型

在基于软件的画图模型中。view 的绘制遵循以下两个步骤:

让view树失效(Invalidate the hierarchy)
绘制view树(Draw the hierarchy)

每当app须要更新UI的一部分的时候,它会在包括了更改内容的view上调用invalidate()方法(或者是它的一个变种方法)。


这个invalidation的消息会沿着view的树形结构一直往上传递,然后计算出屏幕上须要被重绘的区域(dirty区)。
然后,Android系统会绘制view树上全部和dirty区有交集的view。非常不幸的是,这样的画图模型有两种弊端:

首先,在这样的模型下,每个绘制路径都须要运行大量的代码。比方,假设你的app在一个Button上调用了invalidate(),
假设这个Button是位于还有一个view的上面的,android系统也会重绘这个view,虽然它并没有发生不论什么变化。



第二个问题是这样的画图模型可能会隐藏你app里面的bug。

由于android系统会重绘跟dirty区有交集的view。
那么就有可能发生一个你改变了内容的view在你还没有调用invalidate()之前就已经被重绘的情况。
当发生这样的情况的时候。你须要依赖其它正在被invalidated的view才干获取正确的行为。
每次你改动你的app的时候都可能发生这样的事。


正是由于这个原因,一旦改动了影响view绘制的数据或者状态的时候,总是须要在自己定义的view上调用invalidate()。

注意:Android的view在属性发生变化的时候,会自己主动调用invalidate()。比方:背景颜色。TextView的文本内容。

硬件加速画图模型

Android系统仍然使用invalidate()和draw()来请求屏幕更新和渲染view,可是。实际的绘制是不一样的。


android系统会在显示列表内部记录要运行的绘制操作而不是马上就运行这些绘制操作,显示列表包括了view树绘制代码的输出。
还有一个优化是,android系统仅仅须要记录和更新通过调用invalidate()标记为dirty的view的显示列表,
那些没有invalidated的view能够简单地通过之前记录的显示列表来进行重绘。
新的画图模型包括以下三个步骤:

让view树失效(Invalidate the hierarchy)
记录和更新显示列表(Record and update display lists)
绘制显示列表(Draw the display lists)

在这样的模型下。就不能依赖view与dirty区有交集来运行draw()方法。为了确保android系统能记录view的显示列表。必须要调用invalidate()。
假设忘记调用,会导致view看起来没有不论什么变化。就算是view确实被改变了。



使用显示列表对动画的性能也有优点,由于设置特定的属性(比方:alpha或者是rotation)不须要invalidating目标view(这是自己主动完毕的)。
这个优化也应用在有显示列表的view上(启用了硬件加速的app里面的随意的view)
举个样例:假如有一个LinearLayout,包括了一个ListView,以下有一个Button。LinearLayout的显示列表大概是这个样子:
DrawDisplayList(ListView)
DrawDisplayList(Button)

假如如今你想要改变ListView的透明度。

在ListView上调用setAlpha(0.5f)以后,显示列表会变成:
SaveLayerAlpha(0.5)
DrawDisplayList(ListView)
Restore
DrawDisplayList(Button)

ListView的复杂的挥绘制代码并没有运行,相反。系统仅仅须要简单的更新LinearLayout的显示列表。


在没有启用硬件加速的app中,list和它的parent的绘制代码都须要又一次运行一遍。



不支持的绘制操作

启用硬件加度的情况下,2D渲染pipeline支持:大多数常见和不常见的Canvas绘制操作。android自带的全部的用来渲染应用的绘制操作。默认的组件和布局,还有一些高级的视觉效果。比方:映像。文理。



以下的表格展示了不同API level对不同操作的支持级别:


Canvas缩放

硬件加速2D渲染pipeline一開始仅仅支持不缩放的绘制,由于大的缩放值会严重的降低绘制操作的质量。
比方GPU在以scale 1.0做纹理绘制的时候。在API level小于17的时候。这些操作会导致缩放伪影。


以下的表格展示了什么时间实现的能正确处理大范围缩放:


注意:简单的图形是说用不包括PathEffect和非null join(通过调用setStrokeJoin()/setStrokeMiter())的Paint调用
drawRect(),drawCircle(),drawOval(),drawRoundRect(),和drawArc()(用useCenter=false)命令绘制出来的图形。
其它的就是复杂的图形。

假设你的应用被这样的特性或者是限制所影响。能够在受影响的部分通过调用setLayerType(View.LAYER_TYPE_SOFTWARE, null)来关闭硬件加速。
这样的方式使你仍然能够在其它地方利用到硬件加速。

參考“控制硬件加速”章节获取很多其它怎样在app的不同级别启用和禁用硬件加速的信息

View Layers

在全部版本号的Android中。view都能够渲染到off-screen的缓冲区里面去,要么通过使用view的画图缓存。要么使用Canvas.saveLayer()。


Off-screen缓冲区或者叫layers有很多用处。它在复杂view做动画或者是应用复杂的效果的时候能够带来更好的性能。
比方:能够使用Canvas.saveLayer()来实现渐变效果,把view暂时渲染到layer中,然后用透明度合成以后显示到屏幕上。

从Android 3.0(API level 11)開始。能够使用View.setLayerType()方法来更好的控制怎样和什么时候使用layer。
这种方法要两个參数:一个是你想要使用的layer的类型,一个是可选的Paint对象,它描写叙述了应该怎样合成这个layer。
你能够使用Paint參数来应用颜色过滤、混合效果或者是设置layer的透明度。
view能够使用以下三种layer中的一个:

LAYER_TYPE_NONE:view是依照正常的渲染方式进行渲染,不会使用off-screen缓冲区。

这是默认的行为。


LAYER_TYPE_HARDWARE: 假设app启用了硬件加速,view会以硬件方式渲染成硬件texture(The view is rendered in hardware into a hardware texture if the application is hardware accelerated.)。

假设app没有启用硬件加速。LAYER_TYPE_HARDWARE的作用和LAYER_TYPE_SOFTWARE是一样的。
LAYER_TYPE_SOFTWARE: view用软件的方式渲染到bitma中。(The view is rendered in software into a bitmap.)

使用哪一种layer取决于你的目标:

性能:使用LAYER_TYPE_HARDWARE把view渲染成硬件texture。

view被渲染进layer以后。仅仅有到view调用了invalidate()的时候。它的绘制代码才会被运行。有些动画。比方alpha动画能够直接应用到layer中,这对GPU是非常高效的。

视觉效果:使用LAYER_TYPE_HARDWARE或者是LAYER_TYPE_SOFTWARE和Paint给view应用特殊的视觉效果。比方:能够使用ColorMatrixColorFilter仅以黑色和白色来绘制view。

兼容性:使用LAYER_TYPE_SOFTWARE强制view以软件方式进行渲染。假设被硬件加速的view(比方你的整个app都是硬件加速的)渲染出了问题。这是一种非常方便的解决硬件渲染pipeline限制的方式。



View layers和动画

当app启用了硬件加速的时候。Hardware layers能够更快更流畅的传递动画。

让一个复杂的须要非常多绘制操作的view以每秒60帧做动画非常多时候是不可能的。可是使用hardware layers把view渲染成硬件textture能够改善这样的状况。硬件textture可用来给view做动画。降低view在动画过程中重绘的须要。

除非是改变了会调用view的invalidate()的属性,或者是手动调用了invalidate()。否则view是不会被重绘的。假设在你的app中运行动画的时候。没有得到你想要的流畅的效果,考虑在做动画的view上启用hardware layers。

当view背后有hardware layer做支撑的时候,view的一些属性是以把layer合成到屏幕上的方式进行处理的。设置这样的属性是非常高效的,由于他们不须要view的失效重绘。以下的属性列表会应影响layer合成的方式,调用这些属性的set方法会导致优化的失效(optimal invalidation),由于不须要重绘目标view。

alpha: 改变layer的透明度
x, y, translationX, translationY:改变layer的位置
scaleX, scaleY: 改变layer的大小
rotation, rotationX, rotationY: 改变layer在3D空间中的旋转角度
pivotX, pivotY: 改变layer的变换起点

这些属性是当view使用ObjectAnimator做动画的时候使用的名字,假设你想要訪问这些属性。调用他们的set或者是get方法就能够了。


比方,改动alpha属性。能够调用setAlpha()。以下的代码片段展示了在3D中沿着Y轴让view旋转的最有效的方式:
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

由于hardware layers会消耗video memory(这是什么玩意??),极力推荐仅仅有在动画运行期间才启用。动画完毕以后要立刻禁用。
能够用动画监听来做:

View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();
了解很多其它关于动画属性的信息,请參考“动画属性”。

提示和技巧

切换到硬件加速2D图像显示能够立刻就能提升性能,可是。你仍然要遵守以下的规则来设计你的app,才干更有效地使用GPU:

降低app中view的数量
系统要绘制的view的数量越多,系统运行的就越慢。这个对软件渲染pipeline相同适用。

降低view是优化UI的最简单的方式。

避免过度绘制
不要在view上面绘制过多的层。删掉那些全然被上面的不透明的view所遮挡的view。

假设你须要绘制非常多层的view,考虑合并他们到一个层。
如今的硬件上一个好的经验是不要在屏幕上绘制超过一帧2.5倍的像素。



不要在draw方法中创建渲染对象
一种常见的错误是每当渲染方法调用的时候都去创建一个新的Paint或者Path。
这会强制垃圾收集器更频繁的运行,而且会越过硬件pipeline里面的缓存和优化

不要频繁的改动view的形状
复杂的形状、路径、和环形的实例是使用纹理遮罩效果来渲染的。
每当你创建或者是改动path的时候。硬件pipeline都会创建一个新的遮罩,这个代价是非常昂贵的

不要频繁的改动bitmap
每当你改动了bitmap的内容,下次绘制的时候会被当成是一个GPU的纹理被再次上传。

小心的使用alpha
当使用setAlpha(),AlphaAnimation,或者是ObjectAnimator来设置view的透明度的时候。view是在一个off-screen的缓冲区里面进行渲染的。这须要双倍的填充率。
当在非常大的view上应用alpha的时候,要考虑设置view的layer type为:LAYER_TYPE_HARDWARE。

总结一下:
(1)硬件加速是从3.0才開始支持的,它改变了android的画图模型。能提高画图的性能。


(2)view layer从一開始就支持,在复杂view做动画或者是应用复杂的效果的时候能够带来更好的性能。


(3)view layer的一种类型叫做LAYER_TYPE_HARDWARE,启用了硬件加速就用硬件渲染,不启用就用软件渲染。


(4)总之,做动画或者复杂的显示效果的时候,总是设置LAYER_TYPE_HARDWARE,肯定是没有问题的。

例如以下:
View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});

animator.start();

(5)启用硬件加速已知的一些问题,參考:http://www.verydemo.com/cm.jsp?c=26&u=android-ying-jian-jia-su-wen-ti-he-cuo-wu

项目中要调用支付宝的极简支付。出现了花屏view错乱的情况,禁用硬件加速后攻克了。

项目中有个页面非常卡,fps非常低,后来发现是给一个非常大的view设置了alpha。然后加了一句: view.setLayerType(View.LAYER_TYPE_HARDWARE, null);性能发生了翻天覆地的改善。简直不可思议!

(6)view.isHardwareAccelerated()一定要在attached到window以后调用才干得到,在onCreate(),onResume()都是获取不到的。

參考:https://groups.google.com/forum/#!topic/android-developers/vAH0HAZg0uU

附一个android版本号号和API level的相应关系表:


原文:http://developer.android.com/guide/topics/graphics/hardware-accel.html

參考:http://blog.csdn.net/leeo1010/article/details/17913341






猜你喜欢

转载自www.cnblogs.com/mqxnongmin/p/10527941.html