Nishil Patel
Feb 6, 2025
6 min read
In Android, an ANR occurs when your app's main thread is blocked for too long, causing the system to consider it unresponsive. This typically results in the infamous “ANR” dialog that prompts you to either wait or force close the app. Learn more about ANRs in this article.
1.
Introduction
2.
What Blocks the UI Main Thread Causing ANR Errors
3.
How to Diagnose ANR Errors
4.
How to Fix the Error
5.
How to Prevent ANRs
6.
FAQs
While you may be using the latest and greatest Android device, sometimes, things can still go haywire with your apps and the dreaded “Application not responding (ANR)” error shows up on your screen. The app freezes inadvertently, preventing you from interacting with it anyway and leaving you with a bad user experience.
In this article, learn more about what causes ANR errors, practical ways to diagnose and fix them when they occur, and how to prevent them from showing up for your apps.
Image Source: ANRs | Android Developers Portal
ANRs occur when an Android application's main UI rendering thread is either blocked or takes too long to respond (usually over five seconds).
This error primarily occurs when the main UI thread is unable to process user inputs (like touch interactions or key presses) or complete background tasks before the 5-second timeout. This makes the Graphical User Interface (GUI) unresponsive. As a result, the system displays an ANR dialog box giving you the option to either:
Also Read: How to Fix “JavaScript Heap Out of Memory” Error
Let’s look at the common conditions that clog up the UI thread, thus triggering this error:
As described, Android’s main thread does all the heavy lifting for completing UI rendering tasks like processing touch interactions or key presses. Problems arise when an app takes too long to process:
As a result, the user input processing gets delayed, thus, forcing the error.
If the app gets too busy processing other background tasks and a delay of five seconds or more happens for handling user input events, the UI thread gets blocked, thus triggering the ANR dialogue box.
As per Android ground rules, an app needs to finish executing service methods: Service.onCreate(), Service.onStartCommand(), or Service.onBind(), that have initially been declared by it. Even a few seconds delay to complete these operations results in an error message.
If your app code has the Context.startForgoundService() method that’s already declared initially for starting a foreground service, but the startForeground() method doesn’t get called within the five-second window, it throws an error.
When a BroadcastReceiver hasn’t been executed within a specified time frame (within five seconds) for starting an activity in the foreground, it can lead to an ANR. It typically occurs when the:
Even a few seconds delay in returning from the methods JobService.onStartJob() or JobService.onStopJob() can clog the main thread, thus forcing the ANR dialogue. Besides this, if there’s a delay in calling the JobService.setNotification() method just after the JobService.onStartJob() has finished executing, this too, causes the error to show.
Here are a few things to check:
Once you’re good with the diagnosis, you can use the following fixes to resolve the issue:
Check for places in your code where the main thread’s execution speed appears to be taking more than five seconds to complete. For instance, most of the ANRs happen when the onClick(view) handler gets executed by the main thread. To solve this, assign these slow-running handler executions for the Worker Threads instead of the main thread. You can do this by using Android framework classes.
Example Code in Java
Before
@Override
public void onClick(View view) {
// Task on main thread
DataProcessor.process(dataSet);
}
After
@Override
public void onClick(View view) {
// Offload processing to a background thread
new Thread(() -> {
List<ItemType> processedData = DataProcessor.process(dataSet);
// Update UI on main thread
runOnUiThread(() -> {
renderView(processedData);
});
}).start();
}
Example Code in Kotlin
Before
override fun onClick(v: View) {
// Task on main thread
DataProcessor.process(dataSet)
}
After
override fun onClick(v: View) {
// Offload processing to a background coroutine
CoroutineScope(Dispatchers.Default).launch {
val processedData = DataProcessor.process(dataSet)
// Update UI on main thread
withContext(Dispatchers.Main) {
renderView(processedData)
}
}
}
This fix is similar to offloading slow-executing code and methods to worker threads for completion. Moving I/O operations like handling network calls and storage operations for the worker thread to handle should do the trick.
Example Code in Java
Before
void retrieveData() {
String content = dataFetcher.fetchContent(); // I/O on main thread
displayContent(content);
}
After
void retrieveData() {
new Thread(() -> {
String content = dataFetcher.fetchContent(); // I/O on a separate thread
runOnUiThread(() -> {
visualizeContent(content); // Update UI on the main thread
});
}).start();
}
Example Code in Kotlin
Before
fun retrieveData() {
val content = dataFetcher.fetchContent() // I/O on main thread
displayContent(content)
}
After
fun retrieveData() {
CoroutineScope(Dispatchers.IO).launch {
val content = dataFetcher.fetchContent() // I/O on background thread
withContext(Dispatchers.Main) {
visualizeContent(content) // Update UI on the main thread
}
}
}
Sometimes, when a worker thread holds a lock on a resource, the operation is offloaded to the main thread to complete, causing it to block and leading to an ANR error. This refers to lock contention. And this could pose problems with the main thread’s code execution ability.
To resolve lock contention:
Example Code in Java
Before
private synchronized void manipulateResource(String value) {
// Operation that takes time
sharedValue = value;
}
public void onUserAction() {
manipulateResource("Incoming Value"); // Called from UI thread
}
After
private final Object resourceGuardian = new Object();
private void manipulateResource(String value) {
new Thread(() -> {
synchronized (resourceGuardian) {
// Time-consuming operation
sharedValue = value;
runOnUiThread(() -> {
// Optionally, refresh the view
refreshView();
});
}
}).start();
}
public void onUserAction() {
manipulateResource("Incoming Value"); // Invokes the threaded method
}
In the case of deadlocks, use deadlock prevention algorithms (such as the Banker’s Algorithm) to steer clear of circular dependencies. Besides this, maintain a strict order in which threads can use resources to prevent circular waiting.
NOTE: Deadlocks are notoriously difficult to debug and should be avoided through careful design. You can use techniques like resource ordering and avoiding unnecessary synchronization to prevent deadlocks.
To fix slow broadcast receivers:
Example Code in Java
Before
public class NotificationListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Lengthy operation
processNotification(intent.getExtras());
}
}
After
public class NotificationListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
PendingResult taskResult = goAsync();
new Thread(() -> {
handleNotification(intent.getExtras());
taskResult.finish();
}).start();
}
}
Example Code in Kotlin
Before
class NotificationListener : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Lengthy operation
processNotification(intent.extras)
}
}
After
class NotificationListener : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
goAsync().also { asyncTask ->
CoroutineScope(Dispatchers.IO).launch {
handleNotification(intent.extras)
asyncTask.finish()
}
}
}
}
Here are a few ways to prevent ANRs in your Android app:
You can use Android Vitals from the Google Play Console to monitor your app's technical performance, including ANR Rates in production apps. Specifically, the user-perceived ANR rate is a core vital that can affect your app’s discoverability on Google Play. Monitoring these metrics helps you identify and fix issues that impact user experience and your app's visibility.
Use Perfetto to analyze UI responsiveness and pinpoint performance bottlenecks. It’s a powerful profiling tool for Android apps. While Systrace can also be used, Perfetto offers more advanced features and a better interface. You can use Perfetto to visualize main thread activity, identify slow code, and optimize for smoother performance, thus minimizing the risk of ANRs. It’s highly recommended for most developers.
Synchronous code calls to other processes that may take a long time to return can cause ANRs. Avoiding these in your code can help minimize ANRs. Using asynchronous operations works best for tasks that could take a long time to complete.
Use StrictMode during development to find accidental I/O operations on the main thread. StrictMode helps identify areas of the code that may perform long-running operations on the main thread for you to refactor and improve performance.
Optimize code logic to minimize execution time for operations performed on the main thread.
Review and refactor code that might cause lock contention between threads; consider using more granular locking mechanisms or lock-free data structures.
Coroutines provides a structured way to manage background threads and perform asynchronous programming in Kotlin. This can help avoid ANRs.
Set reasonable timeouts for operations that could hang indefinitely, allowing for graceful error handling.
Nishil is a successful serial entrepreneur. He has more than a decade of experience in the software industry. He advocates for a culture of excellence in every software product.
Meet the Author: Nishil Patel, CEO, and Co-founder of BetterBugs. With a passion for innovation and a mission to improve software quality.
We never spam.
Share your experience with the founderhere!