Android ANR: Everything you need to know about

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.

Android ANR feature image

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. 

  1. Application blocks the I/O operations on UI thread for more than 5 sec, so that system can’t process incoming user input events.
  2. 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.

C:\Users\swetabh>adb shell
asus:/ #
asus:/ # cd data/anr/
asus:/data/anr #
asus:/data/anr # ls -l
total 1372
-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'
ABI: 'arm64'
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 com.swetabh.dhcpinfoapp.MainActivity$1.onClick(MainActivity.java:111)
at android.view.View.performClick(View.java:7259)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27896)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7397)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

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.

Note: As of Android P, Loader has been deprecated. Instead, use viewModel with LiveData.

Lock Contention

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.

Deadlock

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
  | stack=0x7fda2af000-0x7fda2b1000 stackSize=8192KB
  | held mutexes=
  at com.android.server.ConnectivityService.getVpnUnderlyingNetworks(ConnectivityService.java:1210)
  - waiting to lock <0x0c418e6b> (a android.util.SparseArray) held by thread 67
  at com.android.server.ConnectivityService.getUnfilteredActiveNetworkState(ConnectivityService.java:1225)
  at com.android.server.ConnectivityService.getActiveNetworkInfoUnfiltered(ConnectivityService.java:1374)
  at com.android.server.net.LockdownVpnTracker.handleStateChangedLocked(LockdownVpnTracker.java:118)
  at com.android.server.net.LockdownVpnTracker.initLocked(LockdownVpnTracker.java:210)
  at com.android.server.net.LockdownVpnTracker.reset(LockdownVpnTracker.java:238)
  - locked <0x03106dc8> (a java.lang.Object)
  at com.android.server.net.LockdownVpnTracker$1.onReceive(LockdownVpnTracker.java:108)
  at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1550)
  at android.app.-$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(lambda:-1)
  at android.os.Handler.handleCallback(Handler.java:883)
  at android.os.Handler.dispatchMessage(Handler.java:100)
. . .
"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
  | stack=0x7b19a48000-0x7b19a4a000 stackSize=1039KB
  | held mutexes=
  at com.android.server.net.LockdownVpnTracker.onVpnStateChanged(LockdownVpnTracker.java:253)
  - waiting to lock <0x03106dc8> (a java.lang.Object) held by thread 1
  at com.android.server.ConnectivityService.notifyLockdownVpn(ConnectivityService.java:6505)
  - locked <0x0c418e6b> (a android.util.SparseArray)
  at com.android.server.ConnectivityService.updateNetworkInfo(ConnectivityService.java:6521)
  at com.android.server.ConnectivityService.access$1700(ConnectivityService.java:230)
  at com.android.server.ConnectivityService$NetworkStateTrackerHandler.maybeHandleNetworkAgentMessage(ConnectivityService.java:2574) 

Slow BroadCast

An ANR occurs in the following cases.

  1. 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.
  2. 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.

Conclusion

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 support@gizmomind.com, and please like our Facebook page.

Swetabh

Swetabh is an Android Developer and a Tech enthusiast. His enthusiasm drives him to know about various technologies. Now he wanted to share his knowledge to other enthusiast who are passionate to know-how and become a GIZMOMIND.

Leave a Reply