page.title=Hardware Acceleration parent.title=Graphics parent.link=index.html @jd:body
Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline supports hardware acceleration, meaning that all drawing operations that are performed on a {@link android.view.View}'s canvas use the GPU. Because of the increased resources required to enable hardware acceleration, your app will consume more RAM.
Hardware acceleration is enabled by default if your Target API level is >=14, but can also be explicitly enabled. If your application uses only standard views and {@link android.graphics.drawable.Drawable}s, turning it on globally should not cause any adverse drawing effects. However, because hardware acceleration is not supported for all of the 2D drawing operations, turning it on might affect some of your custom views or drawing calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly rendered pixels. To remedy this, Android gives you the option to enable or disable hardware acceleration at multiple levels. See Controlling Hardware Acceleration.
If your application performs custom drawing, test your application on actual hardware devices with hardware acceleration turned on to find any problems. The Unsupported drawing operations section describes known issues with hardware acceleration and how to work around them.
You can control hardware acceleration at the following levels:
In your Android manifest file, add the following attribute to the
<application>
tag to enable hardware acceleration for your entire
application:
<application android:hardwareAccelerated="true" ...>
If your application does not behave properly with hardware acceleration turned on globally, you
can control it for individual activities as well. To enable or disable hardware acceleration at
the activity level, you can use the android:hardwareAccelerated
attribute for
the
<activity>
element. The following example enables hardware acceleration for
the entire application but disables it for one activity:
<application android:hardwareAccelerated="true"> <activity ... /> <activity android:hardwareAccelerated="false" /> </application>
If you need even more fine-grained control, you can enable hardware acceleration for a given window with the following code:
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
Note: You currently cannot disable hardware acceleration at the window level.
You can disable hardware acceleration for an individual view at runtime with the following code:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Note: You currently cannot enable hardware acceleration at the view level. View layers have other functions besides disabling hardware acceleration. See View layers for more information about their uses.
It is sometimes useful for an application to know whether it is currently hardware accelerated, especially for things such as custom views. This is particularly useful if your application does a lot of custom drawing and not all operations are properly supported by the new rendering pipeline.
There are two different ways to check whether the application is hardware accelerated:
true
if the {@link android.view.View} is attached to a hardware accelerated
window.true
if the {@link android.graphics.Canvas} is hardware acceleratedIf you must do this check in your drawing code, use {@link android.graphics.Canvas#isHardwareAccelerated Canvas.isHardwareAccelerated()} instead of {@link android.view.View#isHardwareAccelerated View.isHardwareAccelerated()} when possible. When a view is attached to a hardware accelerated window, it can still be drawn using a non-hardware accelerated Canvas. This happens, for instance, when drawing a view into a bitmap for caching purposes.
When hardware acceleration is enabled, the Android framework utilizes a new drawing model that utilizes display lists to render your application to the screen. To fully understand display lists and how they might affect your application, it is useful to understand how Android draws views without hardware acceleration as well. The following sections describe the software-based and hardware-accelerated drawing models.
In the software drawing model, views are drawn with the following two steps:
Whenever an application needs to update a part of its UI, it invokes {@link android.view.View#invalidate invalidate()} (or one of its variants) on any view that has changed content. The invalidation messages are propagated all the way up the view hierarchy to compute the regions of the screen that need to be redrawn (the dirty region). The Android system then draws any view in the hierarchy that intersects with the dirty region. Unfortunately, there are two drawbacks to this drawing model:
Note: Android views automatically call {@link android.view.View#invalidate invalidate()} when their properties change, such as the background color or the text in a {@link android.widget.TextView}.
The Android system still uses {@link android.view.View#invalidate invalidate()} and {@link android.view.View#draw draw()} to request screen updates and to render views, but handles the actual drawing differently. Instead of executing the drawing commands immediately, the Android system records them inside display lists, which contain the output of the view hierarchy’s drawing code. Another optimization is that the Android system only needs to record and update display lists for views marked dirty by an {@link android.view.View#invalidate invalidate()} call. Views that have not been invalidated can be redrawn simply by re-issuing the previously recorded display list. The new drawing model contains three stages:
With this model, you cannot rely on a view intersecting the dirty region to have its {@link android.view.View#draw draw()} method executed. To ensure that the Android system records a view’s display list, you must call {@link android.view.View#invalidate invalidate()}. Forgetting to do so causes a view to look the same even after it has been changed.
Using display lists also benefits animation performance because setting specific properties, such as alpha or rotation, does not require invalidating the targeted view (it is done automatically). This optimization also applies to views with display lists (any view when your application is hardware accelerated.) For example, assume there is a {@link android.widget.LinearLayout} that contains a {@link android.widget.ListView} above a {@link android.widget.Button}. The display list for the {@link android.widget.LinearLayout} looks like this:
Assume now that you want to change the {@link android.widget.ListView}'s opacity. After
invoking setAlpha(0.5f)
on the {@link android.widget.ListView}, the display list now
contains this:
The complex drawing code of {@link android.widget.ListView} was not executed. Instead, the system only updated the display list of the much simpler {@link android.widget.LinearLayout}. In an application without hardware acceleration enabled, the drawing code of both the list and its parent are executed again.
When hardware accelerated, the 2D rendering pipeline supports the most commonly used {@link android.graphics.Canvas} drawing operations as well as many less-used operations. All of the drawing operations that are used to render applications that ship with Android, default widgets and layouts, and common advanced visual effects such as reflections and tiled textures are supported.
The following table describes the support level of various operations across API levels:
First supported API level | ||||
Canvas | ||||
drawBitmapMesh() (colors array) | 18 | |||
drawPicture() | 23 | |||
drawPosText() | 16 | |||
drawTextOnPath() | 16 | |||
drawVertices() | ✗ | |||
setDrawFilter() | 16 | |||
clipPath() | 18 | |||
clipRegion() | 18 | |||
clipRect(Region.Op.XOR) | 18 | |||
clipRect(Region.Op.Difference) | 18 | |||
clipRect(Region.Op.ReverseDifference) | 18 | |||
clipRect() with rotation/perspective | 18 | |||
Paint | ||||
setAntiAlias() (for text) | 18 | |||
setAntiAlias() (for lines) | 16 | |||
setFilterBitmap() | 17 | |||
setLinearText() | ✗ | |||
setMaskFilter() | ✗ | |||
setPathEffect() (for lines) | ✗ | |||
setRasterizer() | ✗ | |||
setShadowLayer() (other than text) | ✗ | |||
setStrokeCap() (for lines) | 18 | |||
setStrokeCap() (for points) | 19 | |||
setSubpixelText() | ✗ | |||
Xfermode | ||||
PorterDuff.Mode.DARKEN (framebuffer) | ✗ | |||
PorterDuff.Mode.LIGHTEN (framebuffer) | ✗ | |||
PorterDuff.Mode.OVERLAY (framebuffer) | ✗ | |||
Shader | ||||
ComposeShader inside ComposeShader | ✗ | |||
Same type shaders inside ComposeShader | ✗ | |||
Local matrix on ComposeShader | 18 |
The hardware accelerated 2D rendering pipeline was built first to support unscaled drawing, with some drawing operations degrading quality significantly at higher scale values. These operations are implemented as textures drawn at scale 1.0, transformed by the GPU. In API level <17, using these operations will result in scaling artifacts increasing with scale.
The following table shows when implementation was changed to correctly handle large scales:Drawing operation to be scaled | First supported API level |
drawText() | 18 |
drawPosText() | ✗ |
drawTextOnPath() | ✗ |
Simple Shapes* | 17 |
Complex Shapes* | ✗ |
drawPath() | ✗ |
Shadow layer | ✗ |
Note: 'Simple' shapes are drawRect()
,
drawCircle()
, drawOval()
, drawRoundRect()
, and
drawArc()
(with useCenter=false) commands issued with a Paint that doesn't have a
PathEffect, and doesn't contain non-default joins (via setStrokeJoin()
/
setStrokeMiter()
). Other instances of those draw commands fall under 'Complex,' in
the above chart.
If your application is affected by any of these missing features or limitations, you can turn off hardware acceleration for just the affected portion of your application by calling {@link android.view.View#setLayerType setLayerType(View.LAYER_TYPE_SOFTWARE, null)}. This way, you can still take advantage of hardware acceleration everywhere else. See Controlling Hardware Acceleration for more information on how to enable and disable hardware acceleration at different levels in your application.
In all versions of Android, views have had the ability to render into off-screen buffers,
either by using a view's drawing cache, or by using {@link android.graphics.Canvas#saveLayer
Canvas.saveLayer()}. Off-screen buffers, or layers, have several uses. You can use them to get
better performance when animating complex views or to apply composition effects. For instance,
you can implement fade effects using Canvas.saveLayer()
to temporarily render a view
into a layer and then composite it back on screen with an opacity factor.
Beginning in Android 3.0 (API level 11), you have more control on how and when to use layers with the {@link android.view.View#setLayerType View.setLayerType()} method. This API takes two parameters: the type of layer you want to use and an optional {@link android.graphics.Paint} object that describes how the layer should be composited. You can use the {@link android.graphics.Paint} parameter to apply color filters, special blending modes, or opacity to a layer. A view can use one of three layer types:
The type of layer you use depends on your goal:
Hardware layers can deliver faster and smoother animations when your application is hardware accelerated. Running an animation at 60 frames per second is not always possible when animating complex views that issue a lot of drawing operations. This can be alleviated by using hardware layers to render the view to a hardware texture. The hardware texture can then be used to animate the view, eliminating the need for the view to constantly redraw itself when it is being animated. The view is not redrawn unless you change the view's properties, which calls {@link android.view.View#invalidate invalidate()}, or if you call {@link android.view.View#invalidate invalidate()} manually. If you are running an animation in your application and do not obtain the smooth results you want, consider enabling hardware layers on your animated views.
When a view is backed by a hardware layer, some of its properties are handled by the way the layer is composited on screen. Setting these properties will be efficient because they do not require the view to be invalidated and redrawn. The following list of properties affect the way the layer is composited. Calling the setter for any of these properties results in optimal invalidation and no redrawing of the targeted view:
alpha
: Changes the layer's opacityx
, y
, translationX
, translationY
:
Changes the layer's positionscaleX
, scaleY
: Changes the layer's sizerotation
, rotationX
, rotationY
: Changes the
layer's orientation in 3D spacepivotX
, pivotY
: Changes the layer's transformations originThese properties are the names used when animating a view with an {@link android.animation.ObjectAnimator}. If you want to access these properties, call the appropriate setter or getter. For instance, to modify the alpha property, call {@link android.view.View#setAlpha setAlpha()}. The following code snippet shows the most efficient way to rotate a viewiew in 3D around the Y-axis:
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator.ofFloat(view, "rotationY", 180).start();
Because hardware layers consume video memory, it is highly recommended that you enable them only for the duration of the animation and then disable them after the animation is done. You can accomplish this using animation listeners:
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();
For more information on property animation, see Property Animation.
Switching to hardware accelerated 2D graphics can instantly increase performance, but you should still design your application to use the GPU effectively by following these recommendations:
LAYER_TYPE_HARDWARE
.