android基础知识

硬件加速

2018-01-18  本文已影响33人  清风流苏

原文链接:https://developer.android.com/guide/topics/graphics/hardware-accel.html

从Android 3.0(API级别 11)开始,Android 2D渲染管道支持硬件加速,这意味着在View画布(Canvas)上执行的所有绘图操作都使用GPU。因为开启硬件加速会有额外的资源消耗,app可能会占用更多的内存。

硬件加速默认在API级别14(含)以上开启,也可以显式开启。如果你的应用仅仅使用一些标准的View和Drawable,则全局打开硬件加速也不会产生任何不利的绘图效果。但是,因为硬件加速并不支持所有的2D绘图操作,因此开启硬件加速可能会影响一些自定义View或绘图调用。问题通常表现为不可见元素、异常或者错误渲染的像素。为了解决这个问题,Android提供了在多个级别上开启或禁用硬件加速的选项。

如果你的应用做了一些自定义绘制,请在开启硬件加速的真机上测试应用来发现问题。

控制硬件加速

可以在四个级别上控制硬件加速:

Application级别

<application>标签的hardwareAccelerated属性为整个应用打开或者关闭硬件加速。优先级最低

<application android:hardwareAccelerated="true" ...>

Activity级别

可以对Activity进行单独控制。优先级高于Application级别。

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

Window级别

如果需要更细致的控制,可以对给定Window开启硬件加速。注意:不支持在Window级别关闭硬件加速。

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

View级别

可以在运行时对单个View关闭硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

注意:当前不能在View级别开启硬件加速。设置View Layer除了禁用硬件加速外还有别的功能。

判断View是否开启了硬件加速

有两种不同方式检查是否应用开启了硬件加速:

如果你自定义View进行绘制的时候需要做检查的话,尽可能优先使用Canvas.isHardwareAccelerated()。因为,当View附着到开启了硬件加速的Window时,它仍然可能使用非硬件加速的Canvas。比如,为了缓存目的,将View绘制到Bitmap时会发生这种情况。

Android绘制模型

当硬件加速开启时,Android框架将采用新的绘制模型,其利用显示列表将应用程序呈现在屏幕上。为了完全理解显示列表(display lists)以及它们如何影响应用程序,理解Android如何在没有硬件加速的模式下绘制View是非常重要的。

基于软件的绘制模型

在软件绘制模型中,View通过下面的两步绘制出来:

每当应用程序需要更新部分UI时,它会在任何更改了内容的View上调用invalidate()(或等效方法)。失效消息在View层级中一路传递,来达到计算出需要重绘的屏幕区域(即脏区域)的目的。Android系统然后重绘在View层级中与脏区域相交的任何View。不幸的是,在这种绘图模型中有两个缺点:

注意:Android View会在其属性发生变化时候自动调用invalidate()方法,比如TextView的文本或者背景颜色发生变化。

硬件加速绘制模型

Android系统仍然使用invalidate()draw()来请求屏幕更新以及渲染View,但是处理实际的绘制有不同。Android系统不是立即执行绘制命令,而是将它们记录到包含了View层级的绘制代码输出的显示列表(display lists)中。另一个优化是,Android系统仅仅为那些通过调用invalide()而被标记成脏的View记录和更新显示列表。没有失效的View仅仅通过重新发布之前记录的显示列表来重新绘制。新的绘制模型包含三步:

在这种模型里,你不能依赖于View与脏区域相交来让它的draw()方法被执行。为了确保Android系统记录View的显示列表,你必须调用其invalidate()方法。忘记调用会导致View显示的和之前一样,即使它的内容已经更改了。

使用显示列也有利于动画性能,因为设置特定属性(比如alpha或旋转)不要求让目标View失效(自动完成的)。此优化也适用于具有显示列表的View(硬件加速开启时的任何View)。比如,假设有一个LinearLayout包含一个ListView和Button,ListView位于Button的上方。这个LinearLayout的显示列表像这样:

假设现在你想要改变这个ListView的透明度。在对ListView调用setAlpha(0.5f)后,显示列表现在包含:

ListView的复杂的绘制代码不会被执行。相反,系统仅仅更新更简单的LinearLayout的显示列表。在没有开启硬件加速的应用中,这个ListView和它的Parent的绘制代码将会再次执行。

不支持的绘制操作

当硬件加速时,2D渲染管道支持最常使用的Canvas绘图操作以及许多较少使用的操作。支持所有用于渲染Android应用程序的绘图操作,默认窗口小部件和布局以及常见的高级视觉效果,例如反射和平铺纹理。

画布缩放(Canvas Scaling)

硬件加速的2D渲染管道是为支持未缩放的绘图而建立的,一些绘图操作在较高的缩放比下会显著降低质量。在缩放比为1.0(未缩放)的情况下,这些操作被实现为纹理绘制(texture drawn),由GPU进行转换。在API级别小于17时,使用这些操作会导致随着缩放比的增加缩放工序增加。

下表显示什么时候支持正确处理大缩放比

需要缩放的绘制操作 开始支持的API级别
drawText() 18
drawPosText() 不支持
drawTextOnPath() 不支持
简单图形 17
复杂图形 不支持
drawPath() 不支持
Shadow layer 不支持

注意:简单图形是指使用不带PathEffect的Paint,不包含默认非默认连接(setStrokeJoin() / setStrokeMiter()),利用drawRect()drawCircle()drawOval()drawRoundRect()drawAcr()(使用userCenter = false)命令绘制而成的图形。其他情况属于复杂图形。

如果你的应用受上面的限制而被影响,你可以对于这些影响点通过调用setLayerType(View.LAYER_TYPE_SOFTWARE, null)来关闭硬件加速。

视图图层

在Android的所有版本,View都可以渲染到离屏缓冲区中,要么使用View的绘制缓冲区,要么使用Canvas.saveLayer()。离屏缓冲区或者图层,有几个应用。可以在对复杂View做动画或者应用组合效果的时候利用他们来获得更好的性能。比如,你可以利用Canvas.saveLayer()临时将View渲染到图层,然后用一个不透明度因子和其组合再渲染回屏幕,来实现渐入渐出效果。

从Android 3.0(API 11)开始,对于何时以及如何使用图层你可以通过View.setLayerType()方法获得更多的控制能力。这个API有两个参数:图层类型,可选的Paint对象用来描述如何组合图层。你可以用Paint参数将颜色过滤器、特殊混合模式或者不透明度应用到图层。View可以使用下面三个图层类型之一:

怎么设置图层类型依赖于你的目标:

视图图层和动画

当应用程序是硬件加速时,硬件层可以带来更快更平滑的动画效果。当对包含很多绘制操作的复杂View做动画想要达到每秒60帧的速度几乎不可能。可以使用硬件层将View渲染成硬件纹理来进行优化。硬件纹理操作可以用作对一个view进行动画操作,当进行动画的时候可以减少对View自身频繁的重绘。除非你改变这个view的属性(调用invalidate()方法)或者你手动的调用invalidate()。如果在你的应用中运行一个动画,但是并没有得到你想要的平滑效果,可以考虑为你要动画的view开启硬件层。

当View由硬件层支持时,一些属性由图层在屏幕上的复合方式操控。设置这些属性的时候是很高效的,因为他们不需要View失效或重绘。下面列出的属性会影响图层的叠加。设置这些属性会使View刷新,但是不会重绘目标View。

这些属性是使用ObjectAnimator对View进行动画操作时用到的名字。如果你想要访问这些属性,调用对应的setter和getter方法。比如,想要修改alpha属性,调用setAlpha()。下面的代码片段展示在3D空间将View绕着Y轴旋转的最高效方式:

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(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

小贴士和技巧

切换到硬件加速的2D图形可以立即提高性能,但您仍然应该按照以下建议来设计应用程序以有效使用GPU:

上一篇 下一篇

猜你喜欢

热点阅读