ANRs

When the UI thread of an Android app is blocked for too long, an "Application
Not Responding" (ANR) error is triggered. If the app is in the foreground, the
system displays a dialog to the user, as shown in figure 1. The ANR dialog gives
the user the opportunity to force quit the app.

Figure 1. ANR dialog displayed to the user

ANRs are a problem because the app’s main thread, which is responsible for
updating the UI, can’t process user input events or draw, causing frustration to
the user. For more information on the app’s main thread, see Processes and
threads.

An ANR will be triggered for your app when one of the following conditions
occur:

While your activity is in the foreground, your app has not responded to an
input event or BroadcastReceiver (such as key press or
screen touch events) within 5 seconds.

While you do not have an activity in the foreground, your BroadcastReceiver hasn't finished executing within a
considerable amount of time.

If your app is experiencing ANRs, you can use the guidance in this article to
diagnose and fix the problem.

Detect and diagnose problems

Android provides several means of letting you know that your
app has a problem, and helping you diagnose it.
If you have already published your app, Android
vitals can alert you that the problem is occurring, and
there are diagnostic tools to help you find the problem.

Android vitals

Android vitals can help improve your app's performance by alerting you, via the
Play
Console, when your app is exhibiting excessive ANRs.
Android vitals considers ANRs excessive when an app:

Exhibits at least one ANR in at least 0.47% of its daily sessions.

Exhibits 2 or more ANRs in at least 0.24% of its daily sessions.

A daily session refers to a day in which your app was used.

For information on how Google Play collects Android vitals data, see the
Play Console documentation.

Diagnosing ANRs

There are some common patterns to look for when diagnosing ANRs:

The app is doing slow operations involving I/O on the main thread.

The app is doing a long calculation on the main thread.

The main thread is doing a synchronous binder call to another process, and
that other process is taking a long time to return.

The main thread is blocked waiting for a synchronized block for a long
operation that is happening on another thread.

The main thread is in a deadlock with another thread, either in your
process or via a binder call. The main thread is not just waiting for a long
operation to finish, but is in a deadlock situation. For more information, see
Deadlock on Wikipedia.

The following techniques can help you find out which of these causes is causing
your ANRs.

Strict mode

Using StrictMode helps you find accidental I/O operations on
the main thread while you’re developing your app. You can use StrictMode at the application or activity level.

Enable background ANR dialogs

Android shows ANR dialogs for apps that take too long to process the broadcast
message only if Show all ANRs is enabled in the device’s Developer
options. For this reason, background ANR dialogs are not always displayed to
the user, but the app could still be experiencing performance issues.

Traceview

You can use Traceview to get a trace of your running app while going through the
use cases and identify the places where the main thread is busy. For information
about how to use Traceview, see Profiling with Traceview and
dmtracedump.

Pull a traces file

Android stores trace information when it experiences an ANR. On older OS
releases, there's a single /data/anr/traces.txt file on the device.
On newer OS releases, there are multiple /data/anr/anr_* files.
You can access ANR traces from a device or emulator by using
Android Debug Bridge (adb) as root:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

You can capture a bug report from a physical device by using either the Take bug
report developer option on the device, or the adb bugreport command on your
development machine. For more information, see Capture and read bug
reports.

Fix the problems

After you have identified the problem, you can use the tips in this section to
fix commonly found problems.

Slow code on the main thread

Identify the places in your code where the app’s main thread is busy for more
than 5 seconds. Look for the suspicious use cases in your app and try to
reproduce the ANR.

For example, figure 2 shows a Traceview timeline where the main thread is busy
for more than 5 seconds.

Figure 2. Traceview timeline showing a busy main thread

Figure 2 shows us that most of the offending code happens in the onClick(View) handler, as shown in the following
code example:

Kotlin

Java

@Override
public void onClick(View view) {
// This task runs on the main thread.
BubbleSort.sort(data);
}

In this case, you should move the work that runs in the main thread to a worker
thread. The Android Framework includes classes that can help to move the task to
a worker thread, for more information, see Helper classes for
threading. The following code shows how
to use AsyncTask helper class to process the task on a worker
thread:

Traceview shows that most of the code runs on a worker thread, as shown in
figure 3. The main thread is available to respond to user events.

Figure 3. Traceview timeline showing the work handled by a worker thread

IO on the main thread

Executing IO operations on the main thread is a common cause of slow operations
on the main thread, which can cause ANRs. It’s recommended to move all IO
operations to a worker thread, as shown in the previous section.

Lock contention

In some scenarios, the work that causes the ANR is not directly executed on the
app’s main thread. If a worker thread holds a lock on a resource that the main
thread requires to complete its work, then an ANR might happen.

For example, figure 4 shows a Traceview timeline where most of the work is
performed on a worker thread.

Figure 4. Traceview timeline that shows the work being executed on a worker
thread

But if your users are still experiencing ANRs, you should look at the status of
the main thread in Android Device Monitor. Usually, the main thread is in the
RUNNABLE state if it’s ready to update the UI and
is generally responsive.

But if the main thread can’t resume execution, then it’s in the BLOCKED status and can’t respond to events. The status
shows on Android Device Monitor as Monitor or Wait, as shown in figure 5.

Figure 5. Main thread in the Monitor status

The following trace shows an app’s main thread that is blocked waiting for a
resource:

Java

@Override
public void onClick(View v) {
// The worker thread holds a lock on lockedResource
new LockTask().execute(data);
synchronized (lockedResource) {
// The main thread requires lockedResource here
// but it has to wait until LockTask finishes using it.
}
}
public class LockTask extends AsyncTask<Integer[], Integer, Long> {
@Override
protected Long doInBackground(Integer[]... params) {
synchronized (lockedResource) {
// This is a long-running operation, which makes
// the lock last for a long time
BubbleSort.sort(params[0]);
}
}
}

Another example is an app’s main thread that is waiting for a result from a
worker thread, as shown in the following code. Note that using wait() and
notify() is not a recommended pattern in Kotlin, which has its own mechanisms
for handling concurrency. When using Kotlin, you should use Kotlin-specific
mechanisms if possible.

There are some other situations that can block the main thread, including
threads that use Lock, Semaphore, as well as a resource pool (such as a
pool of database connections) or other mutual exclusion (mutex) mechanisms.

You should evaluate the locks that your app holds on resources in general, but
if you want to avoid ANRs, then you should look at the locks held for resources
required by the main thread.

Make sure that the locks are held for the least amount of time, or even better,
evaluate whether the app needs the hold in the first place. If you are using the
lock to determine when to update UI based on the processing of a worker thread,
use mechanisms such as onProgressUpdate() and onPostExecute() to communicate between the worker and main threads.

Deadlocks

A deadlock occurs when a thread enters a waiting state because a required
resource is held by another thread, which is also waiting for a resource held by
the first thread. If the app's main thread is in this situation, ANRs are likely
to happen.

Deadlocks are a well-studied phenomenon in computer science, and there are
deadlock prevention algorithms that you can use to avoid deadlocks.

Slow broadcast receivers

Apps can respond to broadcast messages, such as enabling or disabling airplane
mode or a change in connectivity status, by means of broadcast receivers. An ANR
occurs when an app takes too long to process the broadcast message.

An ANR occurs in the following cases:

A broadcast receiver hasn’t finished executing its onReceive() method within a
considerable amount of time.

Your app should only perform short operations in the onReceive() method of a BroadcastReceiver. However, if your app requires more complex
processing as a result of a broadcast message you should defer the task to an
IntentService.

You can use tools like Traceview to identify if your broadcast receiver executes
long-running operations on the app's main thread. For example, figure 6 shows
the timeline of a broadcast receiver that processes a message on the main thread
for approximately 100 seconds.

Figure 6. Traceview timeline showing the BroadcastReceiver work on the main
thread

This behavior can be caused by executing long-running operations on the onReceive() method of the BroadcastReceiver, as shown in the following example:

Java

In situations like these, it’s recommended to move the long-running operation to
an IntentService because it uses a worker thread to execute
its work. The following code shows how to use an IntentService to process a long-running operation:

As a result of using the IntentService, the long-running
operation is executed on a worker thread instead of the main thread. Figure 7
shows the work deferred to the worker thread in the Traceview timeline.

Your broadcast receiver can use goAsync() to signal the system that it needs
more time to process the message. However, you should call finish() on the PendingResult object. The
following example shows how to call finish() to let the system recycle the
broadcast receiver and avoid an ANR: