22cc2764cc
This also moves a few of the documents from API Guides > Best Practices into the new courses for best practices. This is also dependent on CL Ieac8a97a8d6fda41a3682241901150cfe16afc4d which generates the list of classes/lessons on each course landing page. Change-Id: I8132f72f78d844c3b035c7aa269ad3b88a25d02a
197 lines
8.8 KiB
Plaintext
197 lines
8.8 KiB
Plaintext
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>
|