In this post, I will be discussing about what is an Android ANR, what triggers ANR, how to detect and diagnose it and how can we fix it.
You might have come across a scenario, where you are happily using some app. Suddenly you clicked a button, and you feels like it is taking some time to respond, then you tap the button again, but nothing happens. Then out of frustration, you started tapping it multiple times, but nothing happens. After some milliseconds finally your app responded but with a popup saying “Your app isn’t responding”. That’s Android ANR for you- Application Not Responding.
What is ANR in Android?
When the Android detects the system is not able to respond to user input for a few seconds, it throws a dialog saying “Your app isn’t responding”. In genaral, when the UI thread of an android app is blocked for more than 5 sec, Application Not Responding(ANR) dialog is triggered.
What triggers ANR in Android?
In any situation in which your app performs potentially lengthy operations on the main thread or UI thread that makes the system to conclude that your code has frozen, It shows an ANR dialog. There are two possible reasons for ANR to occur.
- Application blocks the I/O operations on UI thread for more than 5 sec, so that system can’t process incoming user input events.
- A Broadcast receiver hasn’t finished executing within 10 sec.
Where to look for an ANR?
There are some common patterns to look for when diagnosing ANRs:
- The app is doing slow operations on the main thread including I/O .
- The app is doing a long calculation on the main thread.
- The app is making a synchronous binder call to another process on the main thread, 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. For more information, see Deadlock on Wikipedia.
How to debug ANR in Android?
Whenever any ANR occurs, a traces.txt file gets generated in /data/anr/ folder on the device.
asus:/ # cd data/anr/
asus:/data/anr # ls -l
-rw-r-xr-x 1 system system 1403003 2020-09-20 13:18 anr_2020-09-20-13-17-58-665
This traces.txt file contains the stack trace of all the methods as well as the information of the state of all the threads in a process that occurred during ANR.
As a developer, we need to look into this file to see what has caused the ANR to occur.
Here is some of the checklist that will help you in debugging the ANR.
- Check whether it is a background ANR or foreground ANR. Priority is for foreground ANR which will cause a visible force close of the app.
- The information related to ANR can also be got from the system logs. Look for the keyword ‘ANR‘ in the logs. It can also be seen in event logs, check for ‘am_anr‘. This will give you the type and the reason for the ANR is logged.
- In order to know why ANR has occurred, always analyze the logs 5 sec/10 sec or 60 sec prior to the ANR has happened.
- Pull the traces.txt file from /data/anr/ folder and analyze it.
- Check if the main thread is in monitor or wait state. If it is, then your main thread is waiting for the resource that has been locked by the worker thread.
- Lastly, from the system logs check, whether the system is overloaded and which process is utilizing most of the CPU.
Example of traces.txt
Here is an example of trace.txt file showing app’s main thread is blocked
Cmd line: com.swetabh.dhcpinfoapp
Build fingerprint: 'XXXX/XXXX/XXX:10/-U00-XX-XX-04/268:eng/test-keys'
Build type: optimized
Zygote loaded classes=9124 post zygote classes=501
Dumping registered class loaders
. . .
DALVIK THREADS (16):
"main" prio=5 tid=1 Runnable
| group="main" sCount=0 dsCount=0 flags=0 obj=0x720c6db0 self=0x7ac1aefc00
| sysTid=10907 nice=-10 cgrp=default sched=0/0 handle=0x7ac302ced0
| state=R schedstat=( 18010016676 275717859 553 ) utm=1773 stm=28 core=6 HZ=100
| stack=0x7fd31b4000-0x7fd31b6000 stackSize=8192KB
| held mutexes= "mutator lock"(shared held)
at java.lang.reflect.Method.invoke(Native method)
As you can see from the example, the traces.txt files contain the stack trace of the method call. It shows, which function and which line number is causing the problem. From the example, in line number 111 of MainActivity.java there is some thing going on which causes the app not to respond for more than 5 sec.
How to fix ANR in Android?
You can use the following tricks to fix ANR in your code.
Move to worker thread
To avoid ANR, you should move the long-running tasks from main thread to worker thread. Android Framework includes some of the classes that will help you in running the code in worker thread. Async Task, Loader are such examples.
Some times it happens that your main thread is waiting for the resource that has been locked by the worker thread. You can pull out the traces.txt file and analyze it.
C:\Users\swetabh>adb pull /data/anr/anr_2020-09-20-13-17-58-665 Desktop
Look at the example below.
... AsyncTask #2" prio=5 tid=18 Runnable | group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100 | sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920 | state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100 | stack=0x94a7e000-0x94a80000 stackSize=1038KB | held mutexes= "mutator lock"(shared held) at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8) at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147) - locked <0x083105ee> (a java.lang.Boolean) at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135) at android.os.AsyncTask$2.call(AsyncTask.java:305) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761) ...
The app’s main thread is blocked and waiting for a resource.
Here is one example showing deadlock between ConnectivityService.java and LockdownVpnTracker.java that I recently encountered.
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x7208ed98 self=0x7c5218dc00
| sysTid=1355 nice=-2 cgrp=default sched=0/0 handle=0x7c536eced0
| state=S schedstat=( 7886578675 2738352455 17915 ) utm=599 stm=188 core=2 HZ=100
| held mutexes=
ConnectivityService. getVpnUnderlyingNetworks( ConnectivityService.java:1210)
- waiting to lock <0x0c418e6b> (a android.util.SparseArray) held by thread 67
ConnectivityService. getUnfilteredActiveNetworkStat e(ConnectivityService.java: 1225)
ConnectivityService. getActiveNetworkInfoUnfiltered (ConnectivityService.java: 1374)
LockdownVpnTracker. handleStateChangedLocked( LockdownVpnTracker.java:118)
- locked <0x03106dc8> (a java.lang.Object)
LockdownVpnTracker$1. onReceive(LockdownVpnTracker. java:108)
ReceiverDispatcher$Args. lambda$getRunnable$0$ LoadedApk$ReceiverDispatcher$ Args(LoadedApk.java:1550)
LoadedApk$ReceiverDispatcher$ Args$_ BumDX2UKsnxLVrE6UJsJZkotuA. run(lambda:-1)
. . .
"ConnectivityServiceThread" prio=5 tid=67 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x14305420 self=0x7b4d72c400
| sysTid=1596 nice=0 cgrp=default sched=0/0 handle=0x7b19b4ad50
| state=S schedstat=( 1082445595 749770275 3598 ) utm=64 stm=42 core=2 HZ=100
| held mutexes=
LockdownVpnTracker. onVpnStateChanged( LockdownVpnTracker.java:253)
- waiting to lock <0x03106dc8> (a java.lang.Object) held by thread 1
ConnectivityService. notifyLockdownVpn( ConnectivityService.java:6505)
- locked <0x0c418e6b> (a android.util.SparseArray)
ConnectivityService. updateNetworkInfo( ConnectivityService.java:6521)
ConnectivityService.access$ 1700(ConnectivityService.java: 230)
ConnectivityService$ NetworkStateTrackerHandler. maybeHandleNetworkAgentMessage (ConnectivityService.java: 2574)
An ANR occurs in the following cases.
- The long-running operation has been performed in the broadcast’s onReceive() method. This causes the Broadcast not to get completed within a considerable amount of time.
goAsync()method has been used in the Broadcast but failed to call
finish()upon completion of the task.
I remember that, once I have encountered an ANR in Broadcast receiver. The receiver was using
goAsync() method on a specific condition, but it was not calling
finish() upon its completion. This causes an ANR to occur.
I hope this gives you brief understanding of What is ANR, how to debug an ANR and what are the tips to follow to fix ANR. If you like this post, please do share it on social media like Facebook or WhatsApp. If you have any suggestions or questions please do let me know in the comment section. Or if you want to know anything about any app or software for your own purpose, write us to email@example.com, and please like our Facebook page.