Diagnosing Android Application Not Responding (ANR) Errors: A Step‑by‑Step Guide
Learn how to recognize ANRs, read trace files, identify causes, apply fixes, and know when to escalate to deeper profiling.
25 Aug 2025, 10:20 UTC

Recognizable ANR condition
When the Android main (UI) thread is blocked for about 5 seconds after an input event or 10 seconds for a foreground broadcast receiver, the system displays the “app isn’t responding” dialog and writes an ANR trace.
Common causes and trace signatures
| Cause | Typical main‑thread stack signature |
|---|---|
| Main‑thread disk or network I/O | Methods such as SQLiteDatabase.query, SharedPreferences.edit().commit(), HttpURLConnection.getInputStream(), OkHttp call.execute() |
| Long‑running computation | Loops, JSON parsing, image decoding, or heavy math on the main thread |
| Lock contention | Object.wait(), Monitor.enter, or java.util.concurrent.locks.Lock.lock() while another thread holds the lock |
| Slow broadcast receiver | onReceive() doing work without goAsync() |
| Binder call timeout | Transact from BinderProxy, or IPC to a service that does not reply |
Ordered diagnostic workflow
- Reproduce and capture the ANR
On a test device, trigger the scenario that leads to the freeze. Then generate a bug report:
adb bugreport > bugreport.zipUnzip the report and locate the trace:
unzip bugreport.zip grep -r "ANR in" bugreport/* # shows the file path, usually under data/anr/traces.txtIf the app is debuggable you can also read the file directly:
adb shell "run-as com.example.package cat /data/anr/traces.txt"Required permissions: USB debugging enabled and the app must be debuggable (or you have root). Risk: bug reports may contain personal data; handle them securely.
- Identify what the main thread was executing
Open the trace and look for the thread named “main”. The top of its stack shows the last user‑space call before the block.
- Check for lock contention with other threads
In the same trace, search for other threads that hold monitors or locks that the main thread is waiting on (look for “waiting to lock” or “owned by”).
- Review recent code changes
Compare the identified method with commits that touched UI‑thread logic, especially any new I/O, synchronization, or broadcast receiver work.
- Correlate with device/OS version
In Google Play Console, open the ANRs & crashes dashboard, filter by app version, and note whether the spike appears on specific API levels or OEM builds.
Fixes mapped to findings
| Finding | Recommended fix |
|---|---|
| Main‑thread disk or network I/O | Move the operation to a background thread, Kotlin coroutine (with Dispatchers.IO), or WorkManager. Replace SharedPreferences.commit() with apply(). |
| Long‑running computation | Offload to a coroutine (Dispatchers.Default) or use AsyncTaskLoader / RxJava compute scheduler. Consider pagination or downsampling for large data. |
| Lock contention | Reduce the scope of synchronized blocks, prefer concurrent data structures (CopyOnWriteArrayList, ConcurrentHashMap), or use Handler/Looper to serialize work instead of raw locks. |
| Slow broadcast receiver | Call goAsync() in onReceive() and finish the work on a background thread, or schedule a JobIntentService/WorkManager. |
| Binder call timeout | Make the service call asynchronous, decrease the payload size, or increase the timeout only if the service is under your control and you can guarantee a quick response. |
Escalation criteria
- ANRs persist after offloading work and applying the fixes above.
- StrictMode in a debug build logs frequent main‑thread violations that are not obvious from the trace.
- Systrace/Perfetto reveals prolonged GPU stalls, excessive binder transaction sizes, or frequent GC pauses correlating with the ANR.
- Play Console shows a high ANR rate on a specific device family, suggesting an OEM‑specific limitation (e.g., slower storage).
When escalation is warranted, enable StrictMode early in Application.onCreate:
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
}
Then use systrace or the Perfetto UI to capture a trace while reproducing the issue and look for long main‑thread waits.
Verification of a fix
- Reproduce the ANR on a test device and confirm that the trace no longer shows the offending main‑thread stack.
- Run a debug build with StrictMode enabled and verify that no main‑thread violations appear in logcat during the scenario.
- Deploy the updated APK to an internal test track, then monitor the ANR rate in Play Console for the new version and compare it to the baseline of the previous version.
Limitations
- ANR timeout values (5 s for input, 10 s for broadcast) can differ on some OEM skins or Android versions; always validate on the target API level.
- Play Console aggregates ANR data and may under‑represent low‑volume issues; supplement with local bug reports.
- StrictMode must never be shipped in a release build because it incurs performance overhead and can expose internal details.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.