Home | History | Annotate | Download | only in performance
      1 page.title=Launch-Time Performance
      2 @jd:body
      3 
      4 <div id="qv-wrapper">
      5 <div id="qv">
      6 
      7 <h2>In this document</h2>
      8 <ol>
      9 <li><a href="#internals">Launch Internals</a>
     10   <ol>
     11     <li><a href="#cold">Cold start</a></li>
     12     <li><a href="#warm">Warm start</a></li>
     13     <li><a href="#lukewarm">Lukewarm start</a></li>
     14   </ol>
     15 </li>
     16 <li><a href="#profiling">Profiling Launch Performance</a>
     17   <ol>
     18     <li><a href="#time-initial">Time to initial display</a></li>
     19     <li><a href="#time-full">Time to full display</a></li>
     20   </ol>
     21 </li>
     22 <li><a href="#common">Common Issues</a>
     23    <ol>
     24       <li><a href="#heavy-app">Heavy app initialization</a></li>
     25       <li><a href="#heavy-act">Heavy activity initialization</a></li>
     26       <li><a href="#themed">Themed launch screens</a></li>
     27    </ol>
     28       </li>
     29 </ol>
     30 </div>
     31 </div>
     32 
     33 <p>
     34 Users expect apps to be responsive and fast to load. An app with a slow startup
     35 time doesnt meet this expectation, and can be disappointing to users. This
     36 sort of poor experience may cause a user to rate your app poorly on the Play
     37 store, or even abandon your app altogether.
     38 </p>
     39 
     40 <p>
     41 This document provides information to help you optimize your apps launch time.
     42 It begins by explaining the internals of the launch process. Next, it discusses
     43 how to profile startup performance. Last, it describes some common startup-time
     44 issues, and gives some hints on how to address them.
     45 </p>
     46 
     47 <h2 id="internals">Launch Internals</h2>
     48 
     49 <p>
     50 App launch can take place in one of three states, each affecting how
     51 long it takes for your app to become visible to the user: cold start,
     52 warm start, and lukewarm start. In a cold start, your app starts from scratch.
     53 In the other states, the system needs to bring the app from the background to
     54 the foreground. We recommend that you always optimize based on an assumption of
     55 a cold start. Doing so can improve the performance of warm and lukewarm starts,
     56 as well.
     57 </p>
     58 
     59 <p>
     60 To optimize your app for fast startup, its useful to understand whats
     61 happening at the system and app levels, and how they interact, in each of
     62 these states.
     63 </p>
     64 
     65 <h3 id="cold">Cold start</h3>
     66 
     67 <p>
     68 A cold start refers to an apps starting from scratch: the systems process
     69 has not, until this start, created the apps process. Cold starts happen in
     70 cases such as your apps being launched for the first time since the device
     71 booted, or since the system killed the app. This type of start presents the
     72 greatest challenge in terms of minimizing startup time, because the system
     73 and app have more work to do than in the other launch states.
     74 </p>
     75 
     76 <p>
     77 At the beginning of a cold start, the system has three tasks. These tasks are:
     78 </p>
     79 
     80 <ol style="1">
     81    <li>Loading and launching the app.</li>
     82    <li>Displaying a blank starting window for the app immediately after launch.
     83    </li>
     84    <li>Creating the app
     85    <a href="{docRoot}guide/components/processes-and-threads.html#Processes">
     86    process.</a></li>
     87 </ol>
     88 <br/>
     89 <p>
     90 As soon as the system creates the app process, the app process is responsible
     91 for the next stages. These stages are:
     92 </p>
     93 
     94 <ol style="1">
     95    <li>Creating the app object.</li>
     96    <li>Launching the main thread.</li>
     97    <li>Creating the main activity.</li>
     98    <li>Inflating views.</li>
     99    <li>Laying out the screen.</li>
    100    <li>Performing the initial draw.</li>
    101 </ol>
    102 
    103 <p>
    104 Once the app process has completed the first draw, the system process swaps
    105 out the currently displayed background window, replacing it with the main
    106 activity. At this point, the user can start using the app.
    107 </p>
    108 
    109 <p>
    110 Figure 1 shows how the system and app processes hand off work between each
    111 other.
    112 </p>
    113 <br/>
    114 
    115   <img src="{@docRoot}topic/performance/images/cold-launch.png">
    116   <p class="img-caption">
    117     <strong>Figure 1.</strong> A visual representation of the important parts of
    118     a cold application launch.
    119   </p>
    120 
    121 <p>
    122 Performance issues can arise during creation of the app and
    123 creation of the activity.
    124 </p>
    125 
    126 <h4 id="app-creation">Application creation</h4>
    127 
    128 <p>
    129 When your application launches, the blank starting window remains on the screen
    130 until the system finishes drawing the app for the first time. At that point,
    131 the system process swaps out the starting window for your app, allowing the
    132 user to start interacting with the app.
    133 </p>
    134 
    135 <p>
    136 If youve overloaded {@link android.app.Application#onCreate() Application.oncreate()}
    137 in your own app, the app starts by calling this
    138 method on your app object. Afterwards, the app spawns the main thread, also
    139 known as the UI thread, and tasks it with creating your main activity.
    140 </p>
    141 
    142 <p>
    143 From this point, system- and app-level processes proceed in accordance with
    144 the <a href="{docRoot}guide/topics/processes/process-lifecycle.html">
    145 app lifecycle stages</a>.
    146 </p>
    147 
    148 <h4 id="act-creation">Activity creation</h4>
    149 
    150 <p>
    151 After the app process creates your activity, the activity performs the
    152 following operations:
    153 </p>
    154 
    155 <ol style="1">
    156    <li>Initializes values.</li>
    157    <li>Calls constructors.</li>
    158    <li>Calls the callback method, such as
    159    {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()},
    160    appropriate to the current lifecycle state of the activity.</li>
    161 </ol>
    162 
    163 <p>
    164 Typically, the
    165 {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}
    166 method has the greatest impact on load time, because it performs the work with
    167 the highest overhead: loading and inflating views, and initializing the objects
    168 needed for the activity to run.
    169 </p>
    170 
    171 <h3 id="warm">Warm start</h3>
    172 
    173 <p>
    174 A warm start of your application is much simpler and lower-overhead than a
    175 cold start. In a warm start, all the system does is bring your activity to
    176 the foreground. If all of your applications activities are still resident in
    177 memory, then the app can avoid having to repeat object initialization, layout
    178 inflation, and rendering.
    179 </p>
    180 
    181 <p>
    182 However, if some memory has been purged in response to memory trimming
    183 events, such as
    184 {@link android.content.ComponentCallbacks2#onTrimMemory(int) onTrimMemory()},
    185 then those objects will need to be recreated in
    186 response to the warm start event.
    187 </p>
    188 
    189 <p>
    190 A warm start displays the same on-screen behavior as a cold start scenario:
    191 The system process displays a blank screen until the app has finished rendering
    192 the activity.
    193 </p>
    194 
    195 <h3 id="lukewarm">Lukewarm start</h3>
    196 
    197 <p>
    198 A lukewarm start encompasses some subset of the operations that
    199 take place during a cold start; at the same time, it represents less overhead
    200 than a warm start. There are many potential states that could be considered
    201 lukewarm starts. For instance:
    202 </p>
    203 
    204 <ul>
    205    <li>The user backs out of your app, but then re-launches it. The process may
    206        have continued to run, but the app must recreate the activity from scratch
    207        via a call to
    208        {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}.</li>
    209 
    210    <li>The system evicts your app from memory, and then the user re-launches it.
    211        The process and the Activity need to be restarted, but the task can
    212        benefit somewhat from the saved instance state bundle passed into
    213        {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}.</li>
    214 </ul>
    215 
    216 <h2 id="profiling">Profiling Launch Performance</h2>
    217 
    218 <p>
    219 In order to properly diagnose start time performance, you can track metrics
    220 that show how long it takes your application to start.
    221 </p>
    222 
    223 <h3 id="time-initial">Time to initial display</h3>
    224 
    225 <p>
    226 From Android 4.4 (API level 19), logcat includes an output line containing
    227 a value called {@code Displayed}. This value represents
    228 the amount of time elapsed between launching the process and finishing drawing
    229 the corresponding activity on the screen. The elapsed time encompasses the
    230 following sequence of events:
    231 </p>
    232 
    233 <ol style="1">
    234    <li>Launch the process.</li>
    235    <li>Initialize the objects.</li>
    236    <li>Create and initialize the activity.</li>
    237    <li>Inflate the layout.</li>
    238    <li>Draw your application for the first time.</li>
    239 </ol>
    240 
    241 <p>
    242 The reported log line looks similar to the following example:
    243 </p>
    244 
    245 <pre class="no-pretty-print">
    246 ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
    247 </pre>
    248 
    249 <p>
    250 If youre tracking logcat output from the command line, or in a terminal,
    251 finding the elapsed time is straightforward. To find elapsed time in
    252 Android Studio, you must disable filters in your logcat view. Disabling the
    253 filters is necessary because the system server, not the app itself, serves
    254 this log.
    255 </p>
    256 
    257 <p>
    258 Once youve made the appropriate settings, you can easily search for the
    259 correct term to see the time. Figure 2 shows how to disable filters, and,
    260 in the second line of output from the bottom, an example of logcat output of
    261 the {@code Displayed} time.
    262 </p>
    263 <br/>
    264 
    265   <img src="{@docRoot}topic/performance/images/displayed-logcat.png">
    266   <p class="img-caption">
    267     <strong>Figure 2.</strong> Disabling filters, and
    268     finding the {@code Displayed} value in logcat.
    269   </p>
    270 
    271 <p>
    272 The {@code Displayed} metric in the logcat output does not necessarily capture
    273 the amount of time until all resources are loaded and displayed: it leaves out
    274 resources that are not referenced in the layout file or that the app creates
    275 as part of object initialization. It excludes these resources because loading
    276 them is an inline process, and does not block the apps initial display.
    277 </p>
    278 
    279 <h3 id="time-full">Time to full display</h3>
    280 
    281 <p>
    282 You can use the {@link android.app.Activity#reportFullyDrawn()} method to
    283 measure the elapsed time
    284 between application launch and complete display of all resources and view
    285 hierarchies. This can be valuable in cases where an app performs lazy loading.
    286 In lazy loading, an app does not block the initial drawing of the window, but
    287 instead asynchronously loads resources and updates the view hierarchy.
    288 </p>
    289 
    290 <p>
    291 If, due to lazy loading, an apps initial display does not include all
    292 resources, you might consider the completed loading and display of all
    293 resources and views as a separate metric: For example, your UI might be
    294 fully loaded, with some text drawn, but not yet display images that the
    295 app must fetch from the network.
    296 </p>
    297 
    298 <p>
    299 To address this concern, you can manually call
    300 {@link android.app.Activity#reportFullyDrawn()}
    301 to let the system know that your activity is
    302 finished with its lazy loading. When you use this method, the value
    303 that logcat displays is the time elapsed
    304 since the creation of the application object, and the moment
    305 {@link android.app.Activity#reportFullyDrawn()} is called.
    306 </p>
    307 
    308 <p>
    309 If you learn that your display times are slower than youd like, you can
    310 go on to try to identify the bottlenecks in the startup process.
    311 </p>
    312 
    313 <h4 id="bottlenecks">Identifying bottlenecks</h4>
    314 
    315 <p>
    316 Two good ways to look for bottlenecks are Android Studios Method Tracer tool
    317 and inline tracing. To learn about Method Tracer, see that tools
    318 <a href="{docRoot}studio/profile/am-methodtrace.html">documentation</a>.
    319 </p>
    320 
    321 <p>
    322 If you do not have access to the Method Tracer tool, or cannot start the tool
    323 at the correct time to gain log information, you can gain similar insight
    324 through inline tracing inside of your apps and activities {@code onCreate()}
    325 methods. To learn about inline tracing, see the reference documentation for
    326 the {@link android.os.Trace} functions, and for the
    327 <a href="{docRoot}studio/profile/systrace-commandline.html">Systrace</a> tool.
    328 </p>
    329 
    330 <h2 id="common">Common Issues</h2>
    331 
    332 <p>
    333 This section discusses several issues that often affect apps startup
    334 performance. These issues chiefly concern initializing app and activity
    335 objects, as well as the loading of screens.
    336 </p>
    337 
    338 <h3 id="heavy-app">Heavy app initialization</h3>
    339 
    340 <p>
    341 Launch performance can suffer when your code overrides the {@code Application}
    342 object, and executes heavy work or complex logic when initializing that object.
    343 Your app may waste time during startup if your Application subclasses perform
    344 initializations that dont need to be done yet. Some initializations may be
    345 completely unnecessary: for example, initializing state information for the
    346 main activity, when the app has actually started up in response to an intent.
    347 With an intent, the app uses only a subset of the previously initialized state
    348 data.
    349 </p>
    350 
    351 <p>
    352 Other challenges during app initialization include garbage-collection events
    353 that are impactful or numerous, or disk I/O happening concurrently with
    354 initialization, further blocking the initialization process. Garbage collection
    355 is especially a consideration with the Dalvik runtime; the Art runtime performs
    356 garbage collection concurrently, minimizing that operation's impact.
    357 </p>
    358 
    359 <h4 id="diagnosing-1">Diagnosing the problem</h4>
    360 
    361 <p>
    362 You can use method tracing or inline tracing to try to diagnose the problem.
    363 </p>
    364 
    365 <h5>Method tracing</h5>
    366 
    367 <p>
    368 Running the Method Tracer tool reveals that the
    369 {@link android.app.Instrumentation#callApplicationOnCreate(android.app.Application) callApplicationOnCreate()}
    370 method eventually calls your {@code com.example.customApplication.onCreate}
    371 method. If the tool shows that these
    372 methods are taking a long time to finish executing, you should explore further
    373 to see what work is occurring there.
    374 </p>
    375 
    376 <h5>Inline tracing</h5>
    377 
    378 <p>
    379 Use inline tracing to investigate likely culprits including:
    380 </p>
    381 
    382 <ul>
    383    <li>Your apps initial {@link android.app.Application#onCreate()}
    384    function.</li>
    385    <li>Any global singleton objects your app initializes.</li>
    386    <li>Any disk I/O, deserialization, or tight loops that might be occurring
    387    during the bottleneck.
    388 </ul>
    389 
    390 
    391 <h4 id="solutions-1">Solutions to the problem</h4>
    392 
    393 <p>
    394 Whether the problem lies with unnecessary initializations or disk I/O,
    395 the solution calls for lazy-initializing objects: initializing only those
    396 objects that are immediately needed. For example, rather than creating global
    397 static objects, instead, move to a singleton pattern, where the app initalizes
    398 objects only the first time it accesses them.
    399 </p>
    400 
    401 <h3 id="heavy-act">Heavy activity initialization</h4>
    402 
    403 <p>
    404 Activity creation often entails a lot of high-overhead work. Often, there are
    405 opportunities to optimize this work to achieve performance improvements. Such
    406 common issues include:
    407 </p>
    408 
    409 <ul>
    410    <li>Inflating large or complex layouts.</li>
    411    <li>Blocking screen drawing on disk, or network I/O.</li>
    412    <li>Loading and decoding bitmaps.</li>
    413    <li>Rasterizing {@link android.graphics.drawable.VectorDrawable VectorDrawable} objects.</li>
    414    <li>Initialization of other subsystems of the activity.</li>
    415 </ul>
    416 
    417 <h4 id="diagnosing-2">Diagnosing the problem</h4>
    418 
    419 <p>
    420 In this case, as well, both method tracing and inline tracing can prove useful.
    421 </p>
    422 
    423 <h5>Method tracing</h5>
    424 
    425 <p>
    426 When running the Method Tracer tool, the particular areas to
    427 focus on your your apps {@link android.app.Application} subclass constructors and
    428 {@code com.example.customApplication.onCreate()} methods.
    429 </p>
    430 
    431 <p>
    432 If the tool shows that these methods are taking a long time to finish
    433 executing, you should explore further to see what work is occurring there.
    434 </p>
    435 
    436 <h5>Inline tracing</h5>
    437 
    438 <p>
    439 Use inline tracing to investigate likely culprits including:
    440 </p>
    441 
    442 <ul>
    443    <li>Your apps initial {@link android.app.Application#onCreate()}
    444    function.</li>
    445    <li>Any global singleton objects it initializes.</li>
    446    <li>Any disk I/O, deserialization, or tight loops that might be occurring
    447    during the bottleneck.</li>
    448 </ul>
    449 
    450 <h4 id="solutions-2">Solutions to the problem</h4>
    451 
    452 <p>
    453 There are many potential bottlenecks, but two common problems and remedies
    454 are as follows:
    455 </p>
    456 
    457 <ul>
    458    <li>The larger your view hierarchy, the more time the app takes to inflate
    459    it. Two steps you can take to address this issue are:
    460 
    461    <ul>
    462       <li>Flattening your view hierarchy by reducing redundant or nested
    463       layouts.</li>
    464 
    465       <li>Not inflating parts of the UI that do not need to be visible during
    466       launch. Instead, use use a {@link android.view.ViewStub} object as a
    467       placeholder for sub-hierarchies that the app can inflate at a more
    468       appropriate time.</li>
    469    </ul>
    470    </li>
    471 
    472    <li>Having all of your resource initialization on the main
    473        thread can also slow down startup. You can address this issue as follows:
    474 
    475    <ul>
    476       <li>Move all resource initialization so that the app can perform it
    477       lazily on a different thread.</li>
    478       <li>Allow the app to load and display your views, and then later
    479       update visual properties that are dependent on bitmaps and other
    480       resources.</li>
    481    </ul>
    482    </li>
    483 
    484 <h3 id="themed">Themed launch screens</h3>
    485 
    486 
    487 <p>
    488 You may wish to theme your apps loading experience, so that the apps
    489 launch screen is thematically consistent with the rest of the app, instead of
    490 with the system theming. Doing so can hide a slow activity launch.
    491 </p>
    492 
    493 <p>
    494 A common way to implement a themed launch screen is to use the the
    495 {@link android.R.attr#windowDisablePreview} theme attribute to turn off
    496 the initial blank screen
    497 that the system process draws when launching the app. However, this approach
    498 can result in a longer startup time than apps that dont suppress the preview
    499 window. Also, it forces the user to wait with no feedback while the activity
    500 launches, making them wonder if the app is functioning properly.
    501 </p>
    502 
    503 <h4 id="diagnosing-3">Diagnosing the problem</h4>
    504 
    505 <p>
    506 You can often diagnose this problem by observing a slow response when a user
    507 launches your app. In such a case, the screen may seem to be frozen, or to
    508 have stopped responding to input.
    509 </p>
    510 
    511 <h4 id="solutions-3">Solutions to the problem</h4>
    512 
    513 <p>
    514 We recommend that, rather than disabling the preview window, you
    515 follow the common
    516 <a href="http://www.google.com/design/spec/patterns/launch-screens.html#">
    517 Material Design</a> patterns. You can use the activity's
    518 {@code windowBackground} theme attribute to provide a simple custom drawable
    519 for the starting activity.
    520 </p>
    521 
    522 <p>
    523 For example, you might create a new drawable file and reference it from the
    524 layout XML and app manifest file as follows:
    525 </p>
    526 
    527 <p>Layout XML file:</p>
    528 
    529 <pre>
    530 &lt;layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"&gt;
    531   &lt;!-- The background color, preferably the same as your normal theme --&gt;
    532   &lt;item android:drawable="@android:color/white"/&gt;
    533   &lt;!-- Your product logo - 144dp color version of your app icon --&gt;
    534   &lt;item&gt;
    535     &lt;bitmap
    536       android:src="@drawable/product_logo_144dp"
    537       android:gravity="center"/&gt;
    538   &lt;/item&gt;
    539 &lt;/layer-list&gt;
    540 </pre>
    541 
    542 <p>Manifest file:</p>
    543 
    544 <pre>
    545 &lt;activity ...
    546 android:theme="@style/AppTheme.Launcher" /&gt;
    547 </pre>
    548 
    549 <p>
    550 The easiest way to transition back to your normal theme is to call
    551 {@link android.view.ContextThemeWrapper#setTheme(int) setTheme(R.style.AppTheme)}
    552 before calling {@code super.onCreate()} and {@code setContentView()}:
    553 </p>
    554 
    555 <pre class="no-pretty-print">
    556 public class MyMainActivity extends AppCompatActivity {
    557   &#64;Override
    558   protected void onCreate(Bundle savedInstanceState) {
    559     // Make sure this is before calling super.onCreate
    560     setTheme(R.style.Theme_MyApp);
    561     super.onCreate(savedInstanceState);
    562     // ...
    563   }
    564 }
    565 </pre>
    566