1page.title=Optimizing the View 2parent.title=Creating Custom Views 3parent.link=index.html 4 5trainingnavtop=true 6previous.title=Making the View Interactive 7previous.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" 30class="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, 38you need to ensure 39that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must 40ensure that your 41animations 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 46by working on 47{@link android.view.View#onDraw onDraw()}, which will give you the biggest payback. In particular 48you should eliminate 49allocations in {@link android.view.View#onDraw onDraw()}, because allocations may lead to a garbage 50collection that 51would cause a stutter. Allocate objects during initialization, or between animations. Never make an 52allocation while an 53animation is running.</p> 54 55<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure 56it's called as 57infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of 58a call to {@link 59android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link 60android.view.View#invalidate() 61invalidate()}. When possible, call the four-parameter variant of {@link 62android.view.View#invalidate() invalidate()} 63rather than the version that takes no parameters. The no-parameter variant invalidates the entire 64view, while the 65four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to 66be more efficient and 67can 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 70android.view.View#requestLayout() 71requestLayout()}, the Android UI system needs to traverse the entire view hierarchy to find out how 72big each view needs 73to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times. 74UI designers 75sometimes create deep hierarchies of nested {@link android.view.ViewGroup ViewGroup} objects in 76order to get the UI to 77behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies 78as shallow as 79possible.</p> 80 81<p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup 82ViewGroup} to perform 83its layout. Unlike the built-in views, your custom view can make application-specific assumptions 84about the size and 85shape of its children, and thus avoid traversing its children to calculate measurements. The 86PieChart example shows how 87to extend {@link android.view.ViewGroup ViewGroup} as part of a custom view. PieChart has child 88views, but it never 89measures them. Instead, it sets their sizes directly according to its own custom layout 90algorithm.</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 95Processing Unit) hardware 96found in most newer Android devices. GPU hardware acceleration can result in a tremendous 97performance increase for many 98applications, but it isn't the right choice for every application. The Android framework 99gives you the ability to finely control which parts of your application are or are not 100hardware 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. 110Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating 111bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To 112get the most out of GPU acceleration, you should maximize the number of operations that the GPU is 113good 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 116each time it's 117rotated 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 122a static 123image. The sample 124defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code 125changes that are needed 126to 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 161shown. During the rest 162of the application's lifetime, the pie chart is cached as an image, and redrawn at different 163rotation angles by the GPU. 164GPU hardware is particularly good at this sort of thing, and the performance difference is 165immediately noticeable.</p> 166 167<p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a 168limited resource. 169For this reason, the final version of {@code PieChart.PieView} only sets its layer type to 170{@link android.view.View#LAYER_TYPE_HARDWARE} 171while the user is actively scrolling. At all other times, it sets its layer type to 172{@link android.view.View#LAYER_TYPE_NONE}, which 173allows 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 176might negatively affect performance on another.</p> 177