Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 1 | page.title=Optimizing the View |
| 2 | parent.title=Creating Custom Views |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | previous.title=Making the View Interactive |
| 7 | previous.link=making-interactive.html |
| 8 | |
| 9 | @jd:body |
| 10 | |
| 11 | <div id="tb-wrapper"> |
| 12 | <div id="tb"> |
| 13 | |
| 14 | <h2>This lesson teaches you to</h2> |
| 15 | <ol> |
| 16 | <li><a href="#less">Do Less, Less Frequently</a></li> |
| 17 | <li><a href="#accelerate">Use Hardware Acceleration</a></li> |
| 18 | </ol> |
| 19 | |
| 20 | <h2>You should also read</h2> |
| 21 | <ul> |
Scott Main | f284d49 | 2012-07-31 09:46:52 -0700 | [diff] [blame] | 22 | <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html"> |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 23 | Hardware Acceleration |
| 24 | </a> |
| 25 | </li> |
| 26 | </ul> |
| 27 | <h2>Try it out</h2> |
| 28 | <div class="download-box"> |
| 29 | <a href="{@docRoot}shareables/training/CustomView.zip" |
| 30 | class="button">Download the sample</a> |
| 31 | <p class="filename">CustomView.zip</p> |
| 32 | </div> |
| 33 | </div> |
| 34 | </div> |
| 35 | |
| 36 | |
| 37 | <p>Now that you have a well-designed view that responds to gestures and transitions between states, |
| 38 | you need to ensure |
| 39 | that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must |
| 40 | ensure that your |
| 41 | animations consistently run at 60 frames per second.</p> |
| 42 | |
| 43 | <h2 id="less">Do Less, Less Frequently</h2> |
| 44 | |
| 45 | <p>To speed up your view, eliminate unnecessary code from routines that are called frequently. Start |
| 46 | by working on |
| 47 | {@link android.view.View#onDraw onDraw()}, which will give you the biggest payback. In particular |
| 48 | you should eliminate |
| 49 | allocations in {@link android.view.View#onDraw onDraw()}, because allocations may lead to a garbage |
| 50 | collection that |
| 51 | would cause a stutter. Allocate objects during initialization, or between animations. Never make an |
| 52 | allocation while an |
| 53 | animation is running.</p> |
| 54 | |
| 55 | <p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure |
| 56 | it's called as |
| 57 | infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of |
| 58 | a call to {@link |
| 59 | android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link |
| 60 | android.view.View#invalidate() |
| 61 | invalidate()}. When possible, call the four-parameter variant of {@link |
| 62 | android.view.View#invalidate() invalidate()} |
| 63 | rather than the version that takes no parameters. The no-parameter variant invalidates the entire |
| 64 | view, while the |
| 65 | four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to |
| 66 | be more efficient and |
| 67 | can eliminate unnecessary invalidation of views that fall outside the invalid rectangle.</p> |
| 68 | |
| 69 | <p>Another very expensive operation is traversing layouts. Any time a view calls {@link |
| 70 | android.view.View#requestLayout() |
| 71 | requestLayout()}, the Android UI system needs to traverse the entire view hierarchy to find out how |
| 72 | big each view needs |
| 73 | to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times. |
| 74 | UI designers |
| 75 | sometimes create deep hierarchies of nested {@link android.view.ViewGroup ViewGroup} objects in |
| 76 | order to get the UI to |
| 77 | behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies |
| 78 | as shallow as |
| 79 | possible.</p> |
| 80 | |
| 81 | <p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup |
| 82 | ViewGroup} to perform |
| 83 | its layout. Unlike the built-in views, your custom view can make application-specific assumptions |
| 84 | about the size and |
| 85 | shape of its children, and thus avoid traversing its children to calculate measurements. The |
| 86 | PieChart example shows how |
| 87 | to extend {@link android.view.ViewGroup ViewGroup} as part of a custom view. PieChart has child |
| 88 | views, but it never |
| 89 | measures them. Instead, it sets their sizes directly according to its own custom layout |
| 90 | algorithm.</p> |
| 91 | |
| 92 | <h2 id="accelerate">Use Hardware Acceleration</h2> |
| 93 | |
| 94 | <p>As of Android 3.0, the Android 2D graphics system can be accelerated by the GPU (Graphics |
| 95 | Processing Unit) hardware |
| 96 | found in most newer Android devices. GPU hardware acceleration can result in a tremendous |
| 97 | performance increase for many |
| 98 | applications, but it isn't the right choice for every application. The Android framework |
| 99 | gives you the ability to finely control which parts of your application are or are not |
| 100 | hardware accelerated.</p> |
| 101 | |
| 102 | <p>See <a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a> |
| 103 | in the Android Developers Guide for directions on how to enable acceleration at the |
| 104 | application, activity, or window level. Notice that in addition to the directions in |
| 105 | the developer guide, you must also set your application's target API to 11 or higher by |
| 106 | specifying {@code <uses-sdk |
| 107 | android:targetSdkVersion="11"/>} in your {@code AndroidManifest.xml} file.</p> |
| 108 | |
| 109 | <p>Once you've enabled hardware acceleration, you may or may not see a performance increase. |
| 110 | Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating |
| 111 | bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To |
| 112 | get the most out of GPU acceleration, you should maximize the number of operations that the GPU is |
| 113 | good at, and minimize the number of operations that the GPU isn't good at.</p> |
| 114 | |
| 115 | <p>In the PieChart example, for instance, drawing the pie is relatively expensive. Redrawing the pie |
| 116 | each time it's |
| 117 | rotated causes the UI to feel sluggish. The solution is to place the pie chart into a child |
| 118 | {@link android.view.View} and set that |
| 119 | {@link android.view.View}'s |
| 120 | <a href="{@docRoot}reference/android/view/View.html#setLayerType(int, android.graphics.Paint)"> |
| 121 | layer type</a> to {@link android.view.View#LAYER_TYPE_HARDWARE}, so that the GPU can cache it as |
| 122 | a static |
| 123 | image. The sample |
| 124 | defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code |
| 125 | changes that are needed |
| 126 | to implement this solution.</p> |
| 127 | |
| 128 | <pre> |
| 129 | private class PieView extends View { |
| 130 | |
| 131 | public PieView(Context context) { |
| 132 | super(context); |
| 133 | if (!isInEditMode()) { |
| 134 | setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | @Override |
| 139 | protected void onDraw(Canvas canvas) { |
| 140 | super.onDraw(canvas); |
| 141 | |
| 142 | for (Item it : mData) { |
| 143 | mPiePaint.setShader(it.mShader); |
| 144 | canvas.drawArc(mBounds, |
| 145 | 360 - it.mEndAngle, |
| 146 | it.mEndAngle - it.mStartAngle, |
| 147 | true, mPiePaint); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | @Override |
| 152 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
| 153 | mBounds = new RectF(0, 0, w, h); |
| 154 | } |
| 155 | |
| 156 | RectF mBounds; |
| 157 | } |
| 158 | </pre> |
| 159 | |
| 160 | <p>After this code change, {@code PieChart.PieView.onDraw()} is called only when the view is first |
| 161 | shown. During the rest |
| 162 | of the application's lifetime, the pie chart is cached as an image, and redrawn at different |
| 163 | rotation angles by the GPU. |
| 164 | GPU hardware is particularly good at this sort of thing, and the performance difference is |
| 165 | immediately noticeable.</p> |
| 166 | |
| 167 | <p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a |
| 168 | limited resource. |
| 169 | For this reason, the final version of {@code PieChart.PieView} only sets its layer type to |
| 170 | {@link android.view.View#LAYER_TYPE_HARDWARE} |
| 171 | while the user is actively scrolling. At all other times, it sets its layer type to |
| 172 | {@link android.view.View#LAYER_TYPE_NONE}, which |
| 173 | allows the GPU to stop caching the image.</p> |
| 174 | |
| 175 | <p>Finally, don't forget to profile your code. Techniques that improve performance on one view |
| 176 | might negatively affect performance on another.</p> |