Home | History | Annotate | Download | only in custom-views
      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 &lt;uses-sdk
    107         android:targetSdkVersion="11"/&gt;} 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        &#64;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        &#64;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