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> 22 <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html"> 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> 177