diff options
| author | 2016-07-01 21:46:33 +0000 | |
|---|---|---|
| committer | 2016-07-01 21:46:33 +0000 | |
| commit | bc8eeafb2c50e92a81fff60576a3bbfae43a4cda (patch) | |
| tree | 0ae9f1834a9f96b21102bacdb73f262e426730d8 | |
| parent | 4ce6754e604abadd8ecd0fe2fa4d503476351cc4 (diff) | |
| parent | a8fa5ffbbc0cea282be4eda5d7df228d88fc67ce (diff) | |
Merge \"Docs: Launch-time performance doc for upcoming Performance section on DAC.\" into nyc-dev
am: a8fa5ffbbc
Change-Id: If224c8c8d0fd3875ad5d8d895577047ead33b5e8
| -rw-r--r-- | docs/html/topic/performance/images/cold-launch.png | bin | 0 -> 12512 bytes | |||
| -rw-r--r-- | docs/html/topic/performance/images/displayed-logcat.png | bin | 0 -> 47099 bytes | |||
| -rw-r--r-- | docs/html/topic/performance/launch-time.jd | 565 |
3 files changed, 565 insertions, 0 deletions
diff --git a/docs/html/topic/performance/images/cold-launch.png b/docs/html/topic/performance/images/cold-launch.png Binary files differnew file mode 100644 index 000000000000..2935ece78665 --- /dev/null +++ b/docs/html/topic/performance/images/cold-launch.png diff --git a/docs/html/topic/performance/images/displayed-logcat.png b/docs/html/topic/performance/images/displayed-logcat.png Binary files differnew file mode 100644 index 000000000000..7dee884d5a0c --- /dev/null +++ b/docs/html/topic/performance/images/displayed-logcat.png diff --git a/docs/html/topic/performance/launch-time.jd b/docs/html/topic/performance/launch-time.jd new file mode 100644 index 000000000000..c9ce1d56185f --- /dev/null +++ b/docs/html/topic/performance/launch-time.jd @@ -0,0 +1,565 @@ +page.title=Launch-Time Performance +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>In this document</h2> +<ol> +<li><a href="#internals">Launch Internals</a> + <ol> + <li><a href="#cold">Cold start</a></li> + <li><a href="#warm">Warm start</a></li> + <li><a href="#lukewarm">Lukewarm start</a></li> + </ol> +</li> +<li><a href="#profiling">Profiling Launch Performance</a> + <ol> + <li><a href="#time-initial">Time to initial display</a></li> + <li><a href="#time-full">Time to full display</a></li> + </ol> +</li> +<li><a href="#common">Common Issues</a> + <ol> + <li><a href="#heavy-app">Heavy app initialization</a></li> + <li><a href="#heavy-act">Heavy activity initialization</a></li> + <li><a href="#themed">Themed launch screens</a></li> + </ol> + </li> +</ol> +</div> +</div> + +<p> +Users expect apps to be responsive and fast to load. An app with a slow startup +time doesn’t meet this expectation, and can be disappointing to users. This +sort of poor experience may cause a user to rate your app poorly on the Play +store, or even abandon your app altogether. +</p> + +<p> +This document provides information to help you optimize your app’s launch time. +It begins by explaining the internals of the launch process. Next, it discusses +how to profile startup performance. Last, it describes some common startup-time +issues, and gives some hints on how to address them. +</p> + +<h2 id="internals">Launch Internals</h2> + +<p> +App launch can take place in one of three states, each affecting how +long it takes for your app to become visible to the user: cold start, +warm start, and lukewarm start. In a cold start, your app starts from scratch. +In the other states, the system needs to bring the app from the background to +the foreground. We recommend that you always optimize based on an assumption of +a cold start. Doing so can improve the performance of warm and lukewarm starts, +as well. +</p> + +<p> +To optimize your app for fast startup, it’s useful to understand what’s +happening at the system and app levels, and how they interact, in each of +these states. +</p> + +<h3 id="cold">Cold start</h3> + +<p> +A cold start refers to an app’s starting from scratch: the system’s process +has not, until this start, created the app’s process. Cold starts happen in +cases such as your app’s being launched for the first time since the device +booted, or since the system killed the app. This type of start presents the +greatest challenge in terms of minimizing startup time, because the system +and app have more work to do than in the other launch states. +</p> + +<p> +At the beginning of a cold start, the system has three tasks. These tasks are: +</p> + +<ol style="1"> + <li>Loading and launching the app.</li> + <li>Displaying a blank starting window for the app immediately after launch. + </li> + <li>Creating the app + <a href="{docRoot}guide/components/processes-and-threads.html#Processes"> + process.</a></li> +</ol> +<br/> +<p> +As soon as the system creates the app process, the app process is responsible +for the next stages. These stages are: +</p> + +<ol style="1"> + <li>Creating the app object.</li> + <li>Launching the main thread.</li> + <li>Creating the main activity.</li> + <li>Inflating views.</li> + <li>Laying out the screen.</li> + <li>Performing the initial draw.</li> +</ol> + +<p> +Once the app process has completed the first draw, the system process swaps +out the currently displayed background window, replacing it with the main +activity. At this point, the user can start using the app. +</p> + +<p> +Figure 1 shows how the system and app processes hand off work between each +other. +</p> +<br/> + + <img src="{@docRoot}performance/images/cold-launch.png"> + <p class="img-caption"> + <strong>Figure 1.</strong> A visual representation of the important parts of + a cold application launch. + </p> + +<p> +Performance issues can arise during creation of the app and +creation of the activity. +</p> + +<h4 id="app-creation">Application creation</h4> + +<p> +When your application launches, the blank starting window remains on the screen +until the system finishes drawing the app for the first time. At that point, +the system process swaps out the starting window for your app, allowing the +user to start interacting with the app. +</p> + +<p> +If you’ve overloaded {@link android.app.Application#onCreate() Application.oncreate()} +in your own app, the app starts by calling this +method on your app object. Afterwards, the app spawns the main thread, also +known as the UI thread, and tasks it with creating your main activity. +</p> + +<p> +From this point, system- and app-level processes proceed in accordance with +the <a href="{docRoot}guide/topics/processes/process-lifecycle.html"> +app lifecycle stages</a>. +</p> + +<h4 id="act-creation">Activity creation</h4> + +<p> +After the app process creates your activity, the activity performs the +following operations: +</p> + +<ol style="1"> + <li>Initializes values.</li> + <li>Calls constructors.</li> + <li>Calls the callback method, such as + {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()}, + appropriate to the current lifecycle state of the activity.</li> +</ol> + +<p> +Typically, the +{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} +method has the greatest impact on load time, because it performs the work with +the highest overhead: loading and inflating views, and initializing the objects +needed for the activity to run. +</p> + +<h3 id="warm">Warm start</h3> + +<p> +A warm start of your application is much simpler and lower-overhead than a +cold start. In a warm start, all the system does is bring your activity to +the foreground. If all of your application’s activities are still resident in +memory, then the app can avoid having to repeat object initialization, layout +inflation, and rendering. +</p> + +<p> +However, if some memory has been purged in response to memory trimming +events, such as +{@link android.content.ComponentCallbacks2#onTrimMemory(int) onTrimMemory()}, +then those objects will need to be recreated in +response to the warm start event. +</p> + +<p> +A warm start displays the same on-screen behavior as a cold start scenario: +The system process displays a blank screen until the app has finished rendering +the activity. +</p> + +<h3 id="lukewarm">Lukewarm start</h3> + +<p> +A lukewarm start encompasses some subset of the operations that +take place during a cold start; at the same time, it represents less overhead +than a warm start. There are many potential states that could be considered +lukewarm starts. For instance: +</p> + +<ul> + <li>The user backs out of your app, but then re-launches it. The process may + have continued to run, but the app must recreate the activity from scratch + via a call to + {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}.</li> + + <li>The system evicts your app from memory, and then the user re-launches it. + The process and the Activity need to be restarted, but the task can + benefit somewhat from the saved instance state bundle passed into + {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}.</li> +</ul> + +<h2 id="profiling">Profiling Launch Performance</h2> + +<p> +In order to properly diagnose start time performance, you can track metrics +that show how long it takes your application to start. +</p> + +<h3 id="time-initial">Time to initial display</h3> + +<p> +From Android 4.4 (API level 19), logcat includes an output line containing +a value called {@code Displayed}. This value represents +the amount of time elapsed between launching the process and finishing drawing +the corresponding activity on the screen. The elapsed time encompasses the +following sequence of events: +</p> + +<ol style="1"> + <li>Launch the process.</li> + <li>Initialize the objects.</li> + <li>Create and initialize the activity.</li> + <li>Inflate the layout.</li> + <li>Draw your application for the first time.</li> +</ol> + +<p> +The reported log line looks similar to the following example: +</p> + +<pre class="no-pretty-print"> +ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms +</pre> + +<p> +If you’re tracking logcat output from the command line, or in a terminal, +finding the elapsed time is straightforward. To find elapsed time in +Android Studio, you must disable filters in your logcat view. Disabling the +filters is necessary because the system server, not the app itself, serves +this log. +</p> + +<p> +Once you’ve made the appropriate settings, you can easily search for the +correct term to see the time. Figure 2 shows how to disable filters, and, +in the second line of output from the bottom, an example of logcat output of +the {@code Displayed} time. +</p> +<br/> + + <img src="{@docRoot}performance/images/displayed-logcat.png"> + <p class="img-caption"> + <strong>Figure 2.</strong> Disabling filters, and + finding the {@code Displayed} value in logcat. + </p> + +<p> +The {@code Displayed} metric in the logcat output does not necessarily capture +the amount of time until all resources are loaded and displayed: it leaves out +resources that are not referenced in the layout file or that the app creates +as part of object initialization. It excludes these resources because loading +them is an inline process, and does not block the app’s initial display. +</p> + +<h3 id="time-full">Time to full display</h3> + +<p> +You can use the {@link android.app.Activity#reportFullyDrawn()} method to +measure the elapsed time +between application launch and complete display of all resources and view +hierarchies. This can be valuable in cases where an app performs lazy loading. +In lazy loading, an app does not block the initial drawing of the window, but +instead asynchronously loads resources and updates the view hierarchy. +</p> + +<p> +If, due to lazy loading, an app’s initial display does not include all +resources, you might consider the completed loading and display of all +resources and views as a separate metric: For example, your UI might be +fully loaded, with some text drawn, but not yet display images that the +app must fetch from the network. +</p> + +<p> +To address this concern, you can manually call +{@link android.app.Activity#reportFullyDrawn()} +to let the system know that your activity is +finished with its lazy loading. When you use this method, the value +that logcat displays is the time elapsed +since the creation of the application object, and the moment +{@link android.app.Activity#reportFullyDrawn()} is called. +</p> + +<p> +If you learn that your display times are slower than you’d like, you can +go on to try to identify the bottlenecks in the startup process. +</p> + +<h4 id="bottlenecks">Identifying bottlenecks</h4> + +<p> +Two good ways to look for bottlenecks are Android Studio’s Method Tracer tool +and inline tracing. To learn about Method Tracer, see that tool’s +<a href="{docRoot}studio/profile/am-methodtrace.html">documentation</a>. +</p> + +<p> +If you do not have access to the Method Tracer tool, or cannot start the tool +at the correct time to gain log information, you can gain similar insight +through inline tracing inside of your apps’ and activities’ {@code onCreate()} +methods. To learn about inline tracing, see the reference documentation for +the {@link android.os.Trace} functions, and for the +<a href="{docRoot}studio/profile/systrace-commandline.html">Systrace</a> tool. +</p> + +<h2 id="common">Common Issues</h2> + +<p> +This section discusses several issues that often affect apps’ startup +performance. These issues chiefly concern initializing app and activity +objects, as well as the loading of screens. +</p> + +<h3 id="heavy-app">Heavy app initialization</h3> + +<p> +Launch performance can suffer when your code overrides the {@code Application} +object, and executes heavy work or complex logic when initializing that object. +Your app may waste time during startup if your Application subclasses perform +initializations that don’t need to be done yet. Some initializations may be +completely unnecessary: for example, initializing state information for the +main activity, when the app has actually started up in response to an intent. +With an intent, the app uses only a subset of the previously initialized state +data. +</p> + +<p> +Other challenges during app initialization include garbage-collection events +that are impactful or numerous, or disk I/O happening concurrently with +initialization, further blocking the initialization process. Garbage collection +is especially a consideration with the Dalvik runtime; the Art runtime performs +garbage collection concurrently, minimizing that operation's impact. +</p> + +<h4 id="diagnosing-1">Diagnosing the problem</h4> + +<p> +You can use method tracing or inline tracing to try to diagnose the problem. +</p> + +<h5>Method tracing</h5> + +<p> +Running the Method Tracer tool reveals that the +{@link android.app.Instrumentation#callApplicationOnCreate(android.app.Application) callApplicationOnCreate()} +method eventually calls your {@code com.example.customApplication.onCreate} +method. If the tool shows that these +methods are taking a long time to finish executing, you should explore further +to see what work is occurring there. +</p> + +<h5>Inline tracing</h5> + +<p> +Use inline tracing to investigate likely culprits including: +</p> + +<ul> + <li>Your app’s initial {@link android.app.Application#onCreate()} + function.</li> + <li>Any global singleton objects your app initializes.</li> + <li>Any disk I/O, deserialization, or tight loops that might be occurring + during the bottleneck. +</ul> + + +<h4 id="solutions-1">Solutions to the problem</h4> + +<p> +Whether the problem lies with unnecessary initializations or disk I/O, +the solution calls for lazy-initializing objects: initializing only those +objects that are immediately needed. For example, rather than creating global +static objects, instead, move to a singleton pattern, where the app initalizes +objects only the first time it accesses them. +</p> + +<h3 id="heavy-act">Heavy activity initialization</h4> + +<p> +Activity creation often entails a lot of high-overhead work. Often, there are +opportunities to optimize this work to achieve performance improvements. Such +common issues include: +</p> + +<ul> + <li>Inflating large or complex layouts.</li> + <li>Blocking screen drawing on disk, or network I/O.</li> + <li>Loading and decoding bitmaps.</li> + <li>Rasterizing {@link android.graphics.drawable.VectorDrawable VectorDrawable} objects.</li> + <li>Initialization of other subsystems of the activity.</li> +</ul> + +<h4 id="diagnosing-2">Diagnosing the problem</h4> + +<p> +In this case, as well, both method tracing and inline tracing can prove useful. +</p> + +<h5>Method tracing</h5> + +<p> +When running the Method Tracer tool, the particular areas to +focus on your your app’s {@link android.app.Application} subclass constructors and +{@code com.example.customApplication.onCreate()} methods. +</p> + +<p> +If the tool shows that these methods are taking a long time to finish +executing, you should explore further to see what work is occurring there. +</p> + +<h5>Inline tracing</h5> + +<p> +Use inline tracing to investigate likely culprits including: +</p> + +<ul> + <li>Your app’s initial {@link android.app.Application#onCreate()} + function.</li> + <li>Any global singleton objects it initializes.</li> + <li>Any disk I/O, deserialization, or tight loops that might be occurring + during the bottleneck.</li> +</ul> + +<h4 id="solutions-2">Solutions to the problem</h4> + +<p> +There are many potential bottlenecks, but two common problems and remedies +are as follows: +</p> + +<ul> + <li>The larger your view hierarchy, the more time the app takes to inflate + it. Two steps you can take to address this issue are: + + <ul> + <li>Flattening your view hierarchy by reducing redundant or nested + layouts.</li> + + <li>Not inflating parts of the UI that do not need to be visible during + launch. Instead, use use a {@link android.view.ViewStub} object as a + placeholder for sub-hierarchies that the app can inflate at a more + appropriate time.</li> + </ul> + </li> + + <li>Having all of your resource initialization on the main + thread can also slow down startup. You can address this issue as follows: + + <ul> + <li>Move all resource initialization so that the app can perform it + lazily on a different thread.</li> + <li>Allow the app to load and display your views, and then later + update visual properties that are dependent on bitmaps and other + resources.</li> + </ul> + </li> + +<h3 id="themed">Themed launch screens</h3> + + +<p> +You may wish to theme your app’s loading experience, so that the app’s +launch screen is thematically consistent with the rest of the app, instead of +with the system theming. Doing so can hide a slow activity launch. +</p> + +<p> +A common way to implement a themed launch screen is to use the the +{@link android.R.attr#windowDisablePreview} theme attribute to turn off +the initial blank screen +that the system process draws when launching the app. However, this approach +can result in a longer startup time than apps that don’t suppress the preview +window. Also, it forces the user to wait with no feedback while the activity +launches, making them wonder if the app is functioning properly. +</p> + +<h4 id="diagnosing-3">Diagnosing the problem</h4> + +<p> +You can often diagnose this problem by observing a slow response when a user +launches your app. In such a case, the screen may seem to be frozen, or to +have stopped responding to input. +</p> + +<h4 id="solutions-3">Solutions to the problem</h4> + +<p> +We recommend that, rather than disabling the preview window, you +follow the common +<a href="http://www.google.com/design/spec/patterns/launch-screens.html#"> +Material Design</a> patterns. You can use the activity's +{@code windowBackground} theme attribute to provide a simple custom drawable +for the starting activity. +</p> + +<p> +For example, you might create a new drawable file and reference it from the +layout XML and app manifest file as follows: +</p> + +<p>Layout XML file:</p> + +<pre> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"> + <!-- The background color, preferably the same as your normal theme --> + <item android:drawable="@android:color/white"/> + <!-- Your product logo - 144dp color version of your app icon --> + <item> + <bitmap + android:src="@drawable/product_logo_144dp" + android:gravity="center"/> + </item> +</layer-list> +</pre> + +<p>Manifest file:</p> + +<pre> +<activity ... +android:theme="@style/AppTheme.Launcher" /> +</pre> + +<p> +The easiest way to transition back to your normal theme is to call +{@link android.view.ContextThemeWrapper#setTheme(int) setTheme(R.style.AppTheme)} +before calling {@code super.onCreate()} and {@code setContentView()}: +</p> + +<pre class="no-pretty-print"> +public class MyMainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + // Make sure this is before calling super.onCreate + setTheme(R.style.Theme_MyApp); + super.onCreate(savedInstanceState); + // ... + } +} +</pre> |