Scott Main | 22cc276 | 2012-11-07 16:35:16 -0800 | [diff] [blame] | 1 | page.title=Keeping Your App Responsive |
Scott Main | 1c2dea0 | 2013-04-10 18:59:29 -0700 | [diff] [blame] | 2 | page.tags="threads","asynctask" |
| 3 | |
Dirk Dougherty | d589421 | 2012-11-28 18:53:10 -0800 | [diff] [blame] | 4 | page.article=true |
Scott Main | 22cc276 | 2012-11-07 16:35:16 -0800 | [diff] [blame] | 5 | @jd:body |
| 6 | |
| 7 | <div id="tb-wrapper"> |
| 8 | <div id="tb"> |
| 9 | |
| 10 | <h2>In this document</h2> |
Scott Main | a3f0e01 | 2013-09-19 17:45:40 -0700 | [diff] [blame] | 11 | <ol class="nolist"> |
Scott Main | 22cc276 | 2012-11-07 16:35:16 -0800 | [diff] [blame] | 12 | <li><a href="#anr">What Triggers ANR?</a></li> |
| 13 | <li><a href="#Avoiding">How to Avoid ANRs</a></li> |
| 14 | <li><a href="#Reinforcing">Reinforcing Responsiveness</a></li> |
| 15 | </ol> |
| 16 | |
| 17 | </div> |
| 18 | </div> |
| 19 | |
| 20 | <div class="figure" style="width:280px"> |
| 21 | <img src="{@docRoot}images/anr.png" alt=""/> |
| 22 | <p class="img-caption"><strong>Figure 1.</strong> An ANR dialog displayed to the user.</p> |
| 23 | </div> |
| 24 | |
| 25 | <p>It's possible to write code that wins every performance test in the world, |
| 26 | but still feels sluggish, hang or freeze for significant periods, or take too |
| 27 | long to process input. The worst thing that can happen to your app's responsiveness |
| 28 | is an "Application Not Responding" (ANR) dialog.</p> |
| 29 | |
| 30 | <p>In Android, the system guards against applications that are insufficiently |
| 31 | responsive for a period of time by displaying a dialog that says your app has |
| 32 | stopped responding, such as the dialog |
| 33 | in Figure 1. At this point, your app has been unresponsive for a considerable |
| 34 | period of time so the system offers the user an option to quit the app. It's critical |
| 35 | to design responsiveness into your application so the system never displays |
| 36 | an ANR dialog to the user. </p> |
| 37 | |
| 38 | <p>This document describes how the Android system determines whether an |
| 39 | application is not responding and provides guidelines for ensuring that your |
| 40 | application stays responsive. </p> |
| 41 | |
| 42 | |
| 43 | <h2 id="anr">What Triggers ANR?</h2> |
| 44 | |
| 45 | <p>Generally, the system displays an ANR if an application cannot respond to |
| 46 | user input. For example, if an application blocks on some I/O operation |
| 47 | (frequently a network access) on the UI thread so the system can't |
| 48 | process incoming user input events. Or perhaps the app |
| 49 | spends too much time building an elaborate in-memory |
| 50 | structure or computing the next move in a game on the UI thread. It's always important to make |
| 51 | sure these computations are efficient, but even the |
| 52 | most efficient code still takes time to run.</p> |
| 53 | |
| 54 | <p>In any situation in which your app performs a potentially lengthy operation, |
| 55 | <strong>you should not perform the work on the UI thread</strong>, but instead create a |
| 56 | worker thread and do most of the work there. This keeps the UI thread (which drives the user |
| 57 | interface event loop) running and prevents the system from concluding that your code |
| 58 | has frozen. Because such threading usually is accomplished at the class |
| 59 | level, you can think of responsiveness as a <em>class</em> problem. (Compare |
| 60 | this with basic code performance, which is a <em>method</em>-level |
| 61 | concern.)</p> |
| 62 | |
| 63 | <p>In Android, application responsiveness is monitored by the Activity Manager |
| 64 | and Window Manager system services. Android will display the ANR dialog |
| 65 | for a particular application when it detects one of the following |
| 66 | conditions:</p> |
| 67 | <ul> |
| 68 | <li>No response to an input event (such as key press or screen touch events) |
| 69 | within 5 seconds.</li> |
| 70 | <li>A {@link android.content.BroadcastReceiver BroadcastReceiver} |
| 71 | hasn't finished executing within 10 seconds.</li> |
| 72 | </ul> |
| 73 | |
| 74 | |
| 75 | |
| 76 | <h2 id="Avoiding">How to Avoid ANRs</h2> |
| 77 | |
| 78 | <p>Android applications normally run entirely on a single thread by default |
| 79 | the "UI thread" or "main thread"). |
| 80 | This means anything your application is doing in the UI thread that |
| 81 | takes a long time to complete can trigger the ANR dialog because your |
| 82 | application is not giving itself a chance to handle the input event or intent |
| 83 | broadcasts.</p> |
| 84 | |
| 85 | <p>Therefore, any method that runs in the UI thread should do as little work |
| 86 | as possible on that thread. In particular, activities should do as little as possible to set |
| 87 | up in key life-cycle methods such as {@link android.app.Activity#onCreate onCreate()} |
| 88 | and {@link android.app.Activity#onResume onResume()}. |
| 89 | Potentially long running operations such as network |
| 90 | or database operations, or computationally expensive calculations such as |
| 91 | resizing bitmaps should be done in a worker thread (or in the case of databases |
| 92 | operations, via an asynchronous request).</p> |
| 93 | |
| 94 | <p>The most effecive way to create a worker thread for longer |
| 95 | operations is with the {@link android.os.AsyncTask} |
| 96 | class. Simply extend {@link android.os.AsyncTask} and implement the |
| 97 | {@link android.os.AsyncTask#doInBackground doInBackground()} method to perform the work. |
| 98 | To post progress changes to the user, you can call |
| 99 | {@link android.os.AsyncTask#publishProgress publishProgress()}, which invokes the |
| 100 | {@link android.os.AsyncTask#onProgressUpdate onProgressUpdate()} callback method. From your |
| 101 | implementation of {@link android.os.AsyncTask#onProgressUpdate onProgressUpdate()} (which |
| 102 | runs on the UI thread), you can notify the user. For example:</p> |
| 103 | |
| 104 | <pre> |
| 105 | private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { |
| 106 | // Do the long-running work in here |
| 107 | protected Long doInBackground(URL... urls) { |
| 108 | int count = urls.length; |
| 109 | long totalSize = 0; |
| 110 | for (int i = 0; i < count; i++) { |
| 111 | totalSize += Downloader.downloadFile(urls[i]); |
| 112 | publishProgress((int) ((i / (float) count) * 100)); |
| 113 | // Escape early if cancel() is called |
| 114 | if (isCancelled()) break; |
| 115 | } |
| 116 | return totalSize; |
| 117 | } |
| 118 | |
| 119 | // This is called each time you call publishProgress() |
| 120 | protected void onProgressUpdate(Integer... progress) { |
| 121 | setProgressPercent(progress[0]); |
| 122 | } |
| 123 | |
| 124 | // This is called when doInBackground() is finished |
| 125 | protected void onPostExecute(Long result) { |
| 126 | showNotification("Downloaded " + result + " bytes"); |
| 127 | } |
| 128 | } |
| 129 | </pre> |
| 130 | |
| 131 | <p>To execute this worker thread, simply create an instance and |
| 132 | call {@link android.os.AsyncTask#execute execute()}:</p> |
| 133 | |
| 134 | <pre> |
| 135 | new DownloadFilesTask().execute(url1, url2, url3); |
| 136 | </pre> |
| 137 | |
| 138 | |
| 139 | <p>Although it's more complicated than {@link android.os.AsyncTask}, you might want to instead |
| 140 | create your own {@link java.lang.Thread} or {@link android.os.HandlerThread} class. If you do, |
| 141 | you should set the thread priority to "background" priority by calling {@link |
| 142 | android.os.Process#setThreadPriority Process.setThreadPriority()} and passing {@link |
| 143 | android.os.Process#THREAD_PRIORITY_BACKGROUND}. If you don't set the thread to a lower priority |
| 144 | this way, then the thread could still slow down your app because it operates at the same priority |
| 145 | as the UI thread by default.</p> |
| 146 | |
| 147 | <p>If you implement {@link java.lang.Thread} or {@link android.os.HandlerThread}, |
| 148 | be sure that your UI thread does not block while waiting for the worker thread to |
| 149 | complete—do not call {@link java.lang.Thread#wait Thread.wait()} or |
| 150 | {@link java.lang.Thread#sleep Thread.sleep()}. Instead of blocking while waiting for a worker |
| 151 | thread to complete, your main thread should provide a {@link |
| 152 | android.os.Handler} for the other threads to post back to upon completion. |
| 153 | Designing your application in this way will allow your app's UI thread to remain |
| 154 | responsive to input and thus avoid ANR dialogs caused by the 5 second input |
| 155 | event timeout.</p> |
| 156 | |
| 157 | <p>The specific constraint on {@link android.content.BroadcastReceiver} execution time |
| 158 | emphasizes what broadcast receivers are meant to do: |
| 159 | small, discrete amounts of work in the background such |
| 160 | as saving a setting or registering a {@link android.app.Notification}. So as with other methods |
| 161 | called in the UI thread, applications should avoid potentially long-running |
| 162 | operations or calculations in a broadcast receiver. But instead of doing intensive |
| 163 | tasks via worker threads, your |
| 164 | application should start an {@link android.app.IntentService} if a |
| 165 | potentially long running action needs to be taken in response to an intent |
| 166 | broadcast.</p> |
| 167 | |
| 168 | <p class="note"><strong>Tip:</strong> |
| 169 | You can use {@link android.os.StrictMode} to help find potentially |
| 170 | long running operations such as network or database operations that |
| 171 | you might accidentally be doing your main thread.</p> |
| 172 | |
| 173 | |
| 174 | |
| 175 | <h2 id="Reinforcing">Reinforce Responsiveness</h2> |
| 176 | |
| 177 | <p>Generally, 100 to 200ms is the threshold beyond which users will perceive |
| 178 | slowness in an application. As such, here |
| 179 | are some additional tips beyond what you should do to avoid ANR and |
| 180 | make your application seem responsive to users:</p> |
| 181 | |
| 182 | <ul> |
| 183 | <li>If your application is doing work in the background in response to |
| 184 | user input, show that progress is being made (such as with a {@link |
| 185 | android.widget.ProgressBar} in your UI).</li> |
| 186 | |
| 187 | <li>For games specifically, do calculations for moves in a worker |
| 188 | thread.</li> |
| 189 | |
| 190 | <li>If your application has a time-consuming initial setup phase, consider |
| 191 | showing a splash screen or rendering the main view as quickly as possible, indicate that |
| 192 | loading is in progress and fill the information asynchronously. In either case, you should |
| 193 | indicate somehow that progress is being made, lest the user perceive that |
| 194 | the application is frozen.</li> |
| 195 | |
| 196 | <li>Use performance tools such as <a href="{@docRoot}tools/help/systrace.html">Systrace</a> |
| 197 | and <a href="{@docRoot}tools/help/traceview.html">Traceview</a> to determine bottlenecks |
| 198 | in your app's responsiveness.</li> |
| 199 | </ul> |