diff options
Diffstat (limited to 'docs/html/training/articles/perf-anr.jd')
| -rw-r--r-- | docs/html/training/articles/perf-anr.jd | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/docs/html/training/articles/perf-anr.jd b/docs/html/training/articles/perf-anr.jd new file mode 100644 index 0000000..864fb34 --- /dev/null +++ b/docs/html/training/articles/perf-anr.jd @@ -0,0 +1,196 @@ +page.title=Keeping Your App Responsive +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>In this document</h2> +<ol> + <li><a href="#anr">What Triggers ANR?</a></li> + <li><a href="#Avoiding">How to Avoid ANRs</a></li> + <li><a href="#Reinforcing">Reinforcing Responsiveness</a></li> +</ol> + +</div> +</div> + +<div class="figure" style="width:280px"> +<img src="{@docRoot}images/anr.png" alt=""/> +<p class="img-caption"><strong>Figure 1.</strong> An ANR dialog displayed to the user.</p> +</div> + +<p>It's possible to write code that wins every performance test in the world, +but still feels sluggish, hang or freeze for significant periods, or take too +long to process input. The worst thing that can happen to your app's responsiveness +is an "Application Not Responding" (ANR) dialog.</p> + +<p>In Android, the system guards against applications that are insufficiently +responsive for a period of time by displaying a dialog that says your app has +stopped responding, such as the dialog +in Figure 1. At this point, your app has been unresponsive for a considerable +period of time so the system offers the user an option to quit the app. It's critical +to design responsiveness into your application so the system never displays +an ANR dialog to the user. </p> + +<p>This document describes how the Android system determines whether an +application is not responding and provides guidelines for ensuring that your +application stays responsive. </p> + + +<h2 id="anr">What Triggers ANR?</h2> + +<p>Generally, the system displays an ANR if an application cannot respond to +user input. For example, if an application blocks on some I/O operation +(frequently a network access) on the UI thread so the system can't +process incoming user input events. Or perhaps the app +spends too much time building an elaborate in-memory +structure or computing the next move in a game on the UI thread. It's always important to make +sure these computations are efficient, but even the +most efficient code still takes time to run.</p> + +<p>In any situation in which your app performs a potentially lengthy operation, +<strong>you should not perform the work on the UI thread</strong>, but instead create a +worker thread and do most of the work there. This keeps the UI thread (which drives the user +interface event loop) running and prevents the system from concluding that your code +has frozen. Because such threading usually is accomplished at the class +level, you can think of responsiveness as a <em>class</em> problem. (Compare +this with basic code performance, which is a <em>method</em>-level +concern.)</p> + +<p>In Android, application responsiveness is monitored by the Activity Manager +and Window Manager system services. Android will display the ANR dialog +for a particular application when it detects one of the following +conditions:</p> +<ul> + <li>No response to an input event (such as key press or screen touch events) + within 5 seconds.</li> + <li>A {@link android.content.BroadcastReceiver BroadcastReceiver} + hasn't finished executing within 10 seconds.</li> +</ul> + + + +<h2 id="Avoiding">How to Avoid ANRs</h2> + +<p>Android applications normally run entirely on a single thread by default +the "UI thread" or "main thread"). +This means anything your application is doing in the UI thread that +takes a long time to complete can trigger the ANR dialog because your +application is not giving itself a chance to handle the input event or intent +broadcasts.</p> + +<p>Therefore, any method that runs in the UI thread should do as little work +as possible on that thread. In particular, activities should do as little as possible to set +up in key life-cycle methods such as {@link android.app.Activity#onCreate onCreate()} +and {@link android.app.Activity#onResume onResume()}. +Potentially long running operations such as network +or database operations, or computationally expensive calculations such as +resizing bitmaps should be done in a worker thread (or in the case of databases +operations, via an asynchronous request).</p> + +<p>The most effecive way to create a worker thread for longer +operations is with the {@link android.os.AsyncTask} +class. Simply extend {@link android.os.AsyncTask} and implement the +{@link android.os.AsyncTask#doInBackground doInBackground()} method to perform the work. +To post progress changes to the user, you can call + {@link android.os.AsyncTask#publishProgress publishProgress()}, which invokes the + {@link android.os.AsyncTask#onProgressUpdate onProgressUpdate()} callback method. From your + implementation of {@link android.os.AsyncTask#onProgressUpdate onProgressUpdate()} (which + runs on the UI thread), you can notify the user. For example:</p> + +<pre> +private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { + // Do the long-running work in here + protected Long doInBackground(URL... urls) { + int count = urls.length; + long totalSize = 0; + for (int i = 0; i < count; i++) { + totalSize += Downloader.downloadFile(urls[i]); + publishProgress((int) ((i / (float) count) * 100)); + // Escape early if cancel() is called + if (isCancelled()) break; + } + return totalSize; + } + + // This is called each time you call publishProgress() + protected void onProgressUpdate(Integer... progress) { + setProgressPercent(progress[0]); + } + + // This is called when doInBackground() is finished + protected void onPostExecute(Long result) { + showNotification("Downloaded " + result + " bytes"); + } +} +</pre> + + <p>To execute this worker thread, simply create an instance and + call {@link android.os.AsyncTask#execute execute()}:</p> + +<pre> +new DownloadFilesTask().execute(url1, url2, url3); +</pre> + + +<p>Although it's more complicated than {@link android.os.AsyncTask}, you might want to instead +create your own {@link java.lang.Thread} or {@link android.os.HandlerThread} class. If you do, +you should set the thread priority to "background" priority by calling {@link +android.os.Process#setThreadPriority Process.setThreadPriority()} and passing {@link +android.os.Process#THREAD_PRIORITY_BACKGROUND}. If you don't set the thread to a lower priority +this way, then the thread could still slow down your app because it operates at the same priority +as the UI thread by default.</p> + +<p>If you implement {@link java.lang.Thread} or {@link android.os.HandlerThread}, +be sure that your UI thread does not block while waiting for the worker thread to +complete—do not call {@link java.lang.Thread#wait Thread.wait()} or +{@link java.lang.Thread#sleep Thread.sleep()}. Instead of blocking while waiting for a worker +thread to complete, your main thread should provide a {@link +android.os.Handler} for the other threads to post back to upon completion. +Designing your application in this way will allow your app's UI thread to remain +responsive to input and thus avoid ANR dialogs caused by the 5 second input +event timeout.</p> + +<p>The specific constraint on {@link android.content.BroadcastReceiver} execution time +emphasizes what broadcast receivers are meant to do: +small, discrete amounts of work in the background such +as saving a setting or registering a {@link android.app.Notification}. So as with other methods +called in the UI thread, applications should avoid potentially long-running +operations or calculations in a broadcast receiver. But instead of doing intensive +tasks via worker threads, your +application should start an {@link android.app.IntentService} if a +potentially long running action needs to be taken in response to an intent +broadcast.</p> + +<p class="note"><strong>Tip:</strong> +You can use {@link android.os.StrictMode} to help find potentially +long running operations such as network or database operations that +you might accidentally be doing your main thread.</p> + + + +<h2 id="Reinforcing">Reinforce Responsiveness</h2> + +<p>Generally, 100 to 200ms is the threshold beyond which users will perceive +slowness in an application. As such, here +are some additional tips beyond what you should do to avoid ANR and +make your application seem responsive to users:</p> + +<ul> + <li>If your application is doing work in the background in response to + user input, show that progress is being made (such as with a {@link + android.widget.ProgressBar} in your UI).</li> + + <li>For games specifically, do calculations for moves in a worker + thread.</li> + + <li>If your application has a time-consuming initial setup phase, consider + showing a splash screen or rendering the main view as quickly as possible, indicate that + loading is in progress and fill the information asynchronously. In either case, you should + indicate somehow that progress is being made, lest the user perceive that + the application is frozen.</li> + + <li>Use performance tools such as <a href="{@docRoot}tools/help/systrace.html">Systrace</a> + and <a href="{@docRoot}tools/help/traceview.html">Traceview</a> to determine bottlenecks + in your app's responsiveness.</li> +</ul> |
