How does the Android system determine whether an application is unresponsive and how to keep the application responsive?
No matter how well the code is written, the application may still experience slow performance, freezing, or taking too long to process input. If the application is running in the foreground and is unresponsive, the system will display an “Application Not Responding” (ANR) dialog, as shown in Figure 1. The ANR dialog allows the user to force quit the application. If the application is not running in the foreground, it will stop silently. It is crucial to consider responsiveness when designing applications to minimize the occurrence of the ANR dialog.
ANR Triggers
Typically, if an application fails to respond to user input on the main thread (also known as the UI thread), the system will display an ANR, preventing the system from processing incoming user input events.
For example, an ANR can occur if the application performs blocking I/O operations (such as network access) on the UI thread. Another example is if the application spends too much time constructing complex in-memory structures or calculating the next move in a game on the UI thread.
In Android, application responsiveness is monitored by the ActivityManager
and WindowManager
system services. When the system detects any of the following conditions, Android will display an ANR dialog for the application:
-
No response to input events (such as key presses or screen taps) within 5 seconds.
-
For foreground intents, the
BroadcastReceiver
does not complete execution within 10 to 20 seconds.
Avoiding ANR
Here are general tips to avoid ANR. For detailed information on diagnosing and debugging different types of ANR, please refer to other pages in this section.
-
Always keep the main thread clear and use threads strategically.
-
Do not perform blocking operations or long-running tasks on the application’s main thread. Instead, create worker threads and perform most operations within them.
-
Try to minimize any lock contention between the main thread and other threads.
-
Minimize any non-UI work done on the main thread; for example, handle broadcasts or run service work. Any methods running on the UI thread must minimize the work done in that thread as much as possible. Especially in key lifecycle methods like
onCreate()
andonResume()
, the activity must minimize the operations required to set up. For detailed information about scheduling work on background threads and passing results back to the UI, refer to the overview of background work. -
Be cautious when sharing thread pools between components. Do not use the same thread for potentially long-running operations and time-sensitive tasks (like broadcast reception).
-
Ensure the application starts quickly. Minimize slow or blocking operations in the application’s startup code, such as methods running during dagger initialization.
-
If you are using
BroadcastReceiver
, consider running the broadcast receiver in a non-main thread usingContext.registerReceiver
. For more details, refer to ANR in BroadcastReceiver. -
If you use
goAsync()
, ensure you callPendingResult.finish
quickly before the ANR timeout.
ANR in BroadcastReceiver
The execution time of BroadcastReceiver
is constrained because the purpose of a broadcast receiver is to perform a small amount of discrete work in the background, such as saving settings or registering a Notification
. Therefore, as with other methods called on the UI thread, the application must avoid performing potentially long-running operations or calculations within the broadcast receiver. Instead of executing long-running tasks on the UI thread, it is better to perform these tasks in the background for later execution.
Another common issue with BroadcastReceiver
objects occurs when they are executed frequently. Frequently executing these objects in the background can reduce the amount of memory available to other applications. For detailed information on efficiently enabling and disabling BroadcastReceiver
objects.
Enhancing Responsiveness
Typically, 100 to 200 milliseconds is a threshold; once exceeded, users will feel that the application is running slowly. Here are more tips to make users feel that your application responds quickly:
-
If the application performs operations in the background to respond to user input, show that the operation is in progress, such as using a
ProgressBar
in the UI. -
Especially in games, calculate moves in a worker thread.
-
If the initial setup phase of the application is very time-consuming, consider displaying a splash screen or presenting the main view as soon as possible, indicating that loading is in progress and asynchronously filling in information. In either case, we recommend indicating that an operation is in progress so that users do not think the application has frozen.
-
Use performance tools like Perfetto and CPU profilers to identify bottlenecks in application responsiveness.
Diagnosing and Fixing ANR
Note that distinguishing between system issues and application issues can be helpful when determining the cause of ANR.
When the system is in a poor state, the following issues may lead to ANR:
-
Temporary issues with the system server often lead to a slowdown in fast binder call speed.
-
System server issues and high device load can prevent the system from scheduling application threads.
Input Scheduling Timeout
An input scheduling ANR occurs when the application’s main thread does not respond to input events (such as swipe or keypress actions) in a timely manner. Since the application runs in the foreground during an input scheduling timeout, users can almost always see such timeouts, making it very important to implement mitigation measures.
The default timeout period: 5 seconds.
Input scheduling ANRs are usually caused by issues on the main thread. If the main thread is blocked and waiting to acquire a lock, it may also involve the thread holding the lock.
To avoid input scheduling ANRs, follow these best practices:
-
Do not perform blocking operations or long-running tasks on the main thread. Consider using
StrictMode
to catch unexpected activities on the main thread. -
Minimize any lock contention between the main thread and other threads.
-
Minimize any non-UI work done on the main thread; for example, work done when handling broadcasts or running services.
Common Causes
Here are some common causes of input scheduling ANRs and suggested solutions.
Cause | Occurrence | Suggested Solution |
---|---|---|
Slow binder calls | Main thread performing synchronous long binder calls. | Move the call off the main thread or try to optimize the call (if you own the API). |
Multiple consecutive binder calls | Main thread performing multiple consecutive synchronous binder calls. | Do not perform binder calls in tight loops. |
Blocking I/O | Main thread performing blocking I/O calls, such as database or network access. | Move all blocking I/O off the main thread. |
Lock contention | Main thread is blocked, waiting to acquire a lock. | Reduce lock contention between the main thread and other threads. Optimize slow code running in other threads. |
Resource-intensive frames | The workload of rendering in a single frame is too large, causing severe stuttering. | Reduce frame rendering workload. Do not use n2 algorithms. Use efficient components to implement operations like scrolling or paging, such as using the Jetpack Paging library. |
Blocked by other components | Another component (like a broadcast receiver) is running and blocking the main thread. | Move non-UI work off the main thread whenever possible. Run broadcast receivers on other threads. |
GPU hang | GPU hangs are system or hardware issues that can block rendering, leading to input scheduling ANRs. | Unfortunately, these issues are often not solvable on the application side. If possible, contact the hardware team to investigate. |
The following flowchart illustrates how to determine the cause of input scheduling ANRs.
No Focus Window
While touch events are sent directly to the relevant window based on click testing, key events require a target. This target is known as the “focus window.” There can only be one focus window on each screen, which is usually the window the user is currently interacting with. If no focus window can be found, input events will trigger a “No Focus Window ANR.” No Focus Window ANRs are a type of input scheduling ANR.
The default timeout period: 5 seconds.
Common Causes
No focus window ANRs are typically caused by one of the following issues:
-
The application is doing too much work too slowly to render the first frame.
-
The main window is not focusable. If a window has the
FLAG_NOT_FOCUSABLE
flag, the user cannot send key or button events to it.
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);}
Broadcast Receiver Timeout
If a broadcast receiver fails to process a broadcast in a timely manner, a broadcast receiver ANR occurs. For synchronous receivers or receivers that do not call goAsync()
, a timeout means that onReceive()
has not completed in a timely manner. For asynchronous receivers or receivers that call goAsync()
, a timeout means that PendingResult.finish()
has not been called in a timely manner.
Broadcast receiver ANRs typically occur in the following threads:
-
Main thread (if the issue is a slow application startup).
-
The thread running the broadcast receiver (if the issue is slow
onReceive()
code). -
The broadcast worker thread (if the issue is slow
goAsync()
broadcast code).
To avoid broadcast receiver ANRs, follow these best practices:
-
Ensure that your application can start quickly, as the application startup time will count towards the ANR timeout if the application starts processing broadcasts.
-
If you use
goAsync()
, ensure you callPendingResult.finish()
quickly. This is subject to the same ANR timeout constraints as synchronous broadcast receivers. -
If you use
goAsync()
, ensure you do not share worker threads with other long-running or blocking operations. -
Consider running the broadcast receiver in a non-main thread using
registerReceiver()
to avoid blocking UI code running on the main thread.
Timeout Period
The timeout period for broadcast reception depends on the platform version and whether the foreground intent flag is set.
Intent Type | Android 13 and lower | Android 14 and higher |
---|---|---|
Foreground priority intent |
10 seconds |
10-20 seconds, depending on whether the process has exhausted CPU |
Background priority intent |
60 seconds |
60-120 seconds, depending on whether the process has exhausted CPU |
To determine if the FLAG_RECEIVER_FOREGROUND
flag is set, look for “flg=” in the ANR body and check for the presence of 0x10000000
. If this bit is set, the intent has the FLAG_RECEIVER_FOREGROUND
flag, so the timeout is shorter.
Examples of ANR bodies with shorter broadcast timeout (10-20 seconds):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Examples of ANR bodies with longer broadcast timeout (60-120 seconds):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
How to Measure Broadcast Time
Broadcast duration measurement starts when system_server
schedules the application and ends when the application finishes processing the broadcast. If the application process is not running, a cold start must also occur within the ANR timeout period. Therefore, slow application startup may lead to broadcast receiver ANRs.
The following diagram illustrates the timeline of broadcast receiver ANRs in relation to certain application processes.
ANR timeout measurement ends when the receiver finishes processing the broadcast: the specific occurrence time depends on whether the receiver is synchronous or asynchronous.
-
For synchronous receivers, the measurement stops when returning from
onReceive()
. -
For asynchronous receivers, the measurement stops when calling
PendingResult.finish()
.
Common Causes
Here are some common causes of broadcast receiver ANRs and suggested solutions.
Cause | Application Object | Occurrence | Suggested Solution |
---|---|---|---|
Slow application startup | All receivers | Application cold startup takes too long. | Optimize for slow application startup. |
onReceive() not dispatched |
All receivers | The broadcast receiver thread is busy performing other work and cannot start onReceive() method. |
Do not perform long-running tasks on the receiver thread (nor move the receiver to a dedicated thread). |
onReceive() running slowly |
All receivers, but mainly synchronous receivers | onReceive() method has started but is blocked or running slowly, thus failing to complete in a timely manner. |
Optimize slow-running receiver code. |
Asynchronous receiver task not dispatched | goAsync() receiver |
onReceive() method attempts to perform work in a blocked worker thread pool, so work is never started. |
Optimize blocking calls or slow-running calls, or use a different thread for the broadcast worker from other long-running tasks. |
Worker running slowly or blocked | goAsync() receiver |
During broadcast processing, a blocking operation or slow operation occurs somewhere in the worker thread pool. Thus, PendingResult.finish is not called in a timely manner. |
Optimize slow-running async receiver code. |
Forgot to call PendingResult.finish |
goAsync() receiver |
Missing call to finish() in the code path. |
Ensure that finish() is always called. |