When it comes to your app’s user experience, nothing is more disruptive and frustrating to your users than app crashes. In fact, crashes are so disruptive that 53% of users uninstall apps that crash, and 80% of users will give a problematic app less than three tries before uninstalling it. But developers are already aware of the impact app crashes can have on their users and actively try to avoid, detect, and fix them with the help of various tools.
However, there is another type of error that can be just as disruptive as app crashes but nevertheless relatively manages to fly under the radar. You may have encountered this problem before as an ANR error. But what are ANRs? Application Not Responding (ANR) errors might not cause your app to crash, but their impact on your app’s user experience is just as bad. In this post, we will look into what exactly ANRs are and how to avoid having them happen to your app.
What are ANRs?
ANR errors happen when your app becomes unresponsive to user input for more than five seconds or when a broadcast receiver is unable to finish executing its
onReceive() method within 10 seconds. Instead of force-quitting your app, this causes the OS to show a prompt to your user that lets them know that the app is not responding and gives them a chance to force-quit your app or wait for it to become responsive.
While there might not be any statistics available about what percentage of users choose each option, it is probably safe to say that many, if not most, users decide to force-quit your app and relaunch it. And for those who decide to wait but get disappointed when the app fails to recover, it is arguably twice as bad as a crash.
To capture ANR errors, you’ll need a crash reporting tool with ANR detection. Instabug Crash Reporting is capable of detecting crash-like errors such as ANRs and OOMs (out of memory errors) and helps you find out what’s causing them.
What causes ANRs and how to avoid them
Now we understand what ANRs are. As we mentioned earlier, ANRs are caused by your main thread becoming blocked from responding to user input for more than five seconds or when a broadcast receiver doesn’t finish executing within 10 seconds. This can happen for five different reasons, each of which requires different strategies to avoid.
Running slow code on the main thread
One of the most obvious possible reasons for ANRs is running code that might need more than five seconds to execute on your app’s main thread. This causes your app’s UI to become unresponsive to user input until that is done. In this case, if your user decides to wait instead of force-quitting your app, the main thread might get enough time to become responsive again.
To avoid getting your app’s main thread blocked, you should always use a worker thread to run heavier workloads so your app remains responsive to your users. Also, consider showing your users a progress bar of some sort to let them know that you’re working on it.
Executing I/O operations on the main thread
I/O operations like network and storage operations can be slow and their performance can be unpredictable. This is why executing I/O operations on the main thread is a common cause of ANRs. Like the previous reason, if your user decides to wait instead of force-quitting your app, they might be rewarded with your app becoming responsive again if they’re patient.
I/O operations are another good candidate for worker threads. Running them in a worker thread will solve this issue and allow your UI to remain responsive while I/O operations get a chance to execute.
Mismanaging lock states
Another common cause of ANRs is when the main thread is not able to finish its work because a worker thread has a lock on a resource that is needed by the main thread. In this case, the main thread is forced to wait for the worker thread to finish its work, which can take considerably longer than five seconds. This can also happen if your main thread is waiting for a result from a worker thread to complete its work. Your app might still recover if your users decide against force-quitting it, but depending on how long that worker thread is going to take, there’s a good chance they’ll run out of patience.
To avoid this state, you should evaluate the locks your app has on resources, especially those that are needed by the main thread. Avoid using locks on resources whenever possible and when you do use them, try to keep them for the shortest possible time.
A deadlock occurs when the main thread is waiting for a resource that is locked by another thread which in turn is waiting for a resource that is locked by the main thread. This is the only case where your app’s UI will remain unresponsive indefinitely and it doesn’t matter how long your users decide to wait before force quitting your app.
Like the previous cause, you need to evaluate your app’s resource locks and only use them when necessary and for the shortest time needed.
Slow broadcast receivers
If you use the
onReceive() method of a
BroadcastReceiver to run complex operations, this can cause the main thread to become blocked until that work is done. In this case, it is possible that your app will recover if your users give it enough time, but again, the longer it takes for the app to recover, the higher the chances your app gets force-quit.
To make sure this doesn’t happen, make sure you use broadcast receivers for light, short operations. If you need to run a more complex operation, you should consider deferring the task to an
IntentService because it uses a worker thread to execute its work.
Monitoring and understanding your ANRs
To start catching ANR errors with Instabug, all you need to do is use Crash Reporting for Android. ANR error detection is enabled by default and Instabug will start reporting them automatically. When you go to the Crash Reporting page on your dashboard, you will see the number of sessions that encountered an ANR error, and you can include these sessions in your crash-free sessions rate calculation.
Along with each ANR error report, you will receive all the information you need to fix the issue. Instabug automatically includes console logs, network logs, repro steps, and a profile of the state of the device for the 60 seconds before the error occurred.
- How to Get the Most Out of Instabug Crash Reports
- Benchmarking Crashing Sessions for Mobile Apps (What Is a Good Crash Rate?)
- Keep Users Loyal by Replying to Crash Reports
- Why Does Your App Keep Crashing?