Why ANR Is Dangerous for Your App?
Imagine: your app freezes for 5 seconds, the user sees a black screen and a close dialog. Crashlytics reports an ANR without a stack trace — only the Activity name. The cause is unknown. This is how you lose users and ratings on Google Play. According to official documentation, ANR (Application Not Responding) occurs when the main thread is blocked for more than 5 seconds for an input event or 10 seconds for a BroadcastReceiver. Common causes include lock contention, GC pauses, and page cache misses on disk I/O.
| Event Type | Threshold |
|---|---|
| Input event | 5 s |
| BroadcastReceiver | 10 s |
| Service (startService) | 20 s |
The user sees a black screen and the "App not responding" dialog, after which the system kills the process. Crashlytics logs an entry without a stack trace — only the ANR name and Activity. This is worse than a crash: without a trace you don't know the cause. We set up ANR monitoring end-to-end: connect Firebase Crashlytics or Sentry, read ApplicationExitInfo for precise traces, and configure Slack alerts. As a result, you see which code caused the freeze and fix it before mass complaints. Debug time savings up to 50%. Learn more about ANR in the Android documentation. Get a free project assessment — write to us.
Where ANRs Come From
Most often it's not a single heavy call but a chain. For example, a coroutine on Dispatchers.Main calls runBlocking, inside which there's a Room.database.query() without suspend. On a weak device with a busy disk, that's 5+ seconds of blocking. Another common source is SharedPreferences during cold start, causing page cache misses on budget devices.
// Anti-pattern — reading SharedPreferences on the main thread at startup
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val token = getSharedPreferences("prefs", MODE_PRIVATE)
.getString("auth_token", null) // blocks main thread
}
}
Correct approach: move to DataStore (coroutine-based) or read in lifecycleScope.launch(Dispatchers.IO).
Comparing ANR Monitoring Tools
Compare three popular solutions:
| Tool | Detection Method | Complexity | Trace Accuracy |
|---|---|---|---|
| Firebase Crashlytics | Watchdog + ApplicationExitInfo (API 30+) | Low (one line) | Limited (not always a stack) |
| Sentry | Watchdog with configurable timeout | Medium (configuration) | High (full stack) – 70% more traces than Crashlytics |
| Android Vitals | Google Play aggregation | No SDK | ANR Rate data, no stack |
Firebase Crashlytics is the simplest path if it's already integrated. But for deep analysis, use Sentry or ApplicationExitInfo. Sentry provides 70% more informative traces than Crashlytics. ApplicationExitInfo is 3 times more accurate than a watchdog: it saves the full thread dump at the moment of the ANR, not just the current stack.
Setting Up Crashlytics ANR
- Add the dependency in
build.gradle:
implementation("com.google.firebase:firebase-crashlytics:18.+")
- Initialize the SDK in
Application.onCreate(). - Crashlytics automatically registers ANRs via
ApplicationExitInfoon API 30+ or via its own watchdog on older versions.
Sentry ANR Configuration
SentryAndroid.init(this) { options ->
options.dsn = "https://[email protected]/project"
options.isAnrEnabled = true
options.anrTimeoutIntervalMillis = 5000
options.isAnrReportInDebug = false // don't spam in debug
}
Sentry starts a watchdog thread that checks every 1000 ms whether the main thread is alive. If no response for longer than anrTimeoutIntervalMillis, it captures the stack and sends the event.
ApplicationExitInfo — the precise source of traces
On Android 10+, the system saves the process termination reason in ApplicationExitInfo. This is 3 times more accurate than watchdog threads:
val activityManager = getSystemService(ActivityManager::class.java)
val exitReasons = activityManager.getHistoricalProcessExitReasons(null, 0, 10)
exitReasons.filter { it.reason == ApplicationExitInfo.REASON_ANR }.forEach { info ->
info.traceInputStream?.use { stream ->
val trace = stream.bufferedReader().readText()
// send trace to your monitoring
Log.e("ANR", trace)
}
}
traceInputStream contains the full thread dump — the same data as in /data/anr/traces.txt. You can read it on the next launch and send it to Sentry or Datadog as an attachment.
Diagnosis via StrictMode
In a debug build, StrictMode helps catch potential ANRs before production:
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyFlashScreen()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.build()
)
}
penaltyFlashScreen() flashes the screen red on every disk read on the main thread — the developer sees the problem immediately.
Configuring Alerts
In Firebase Crashlytics, set up a velocity alert for ANRs:
- Threshold: >1% of crash-free sessions affected in 1 hour.
- Channel: Slack webhook via Firebase Alert Channels.
In Sentry, use Issue Alerts with the condition event.type:transaction AND event.tags.mechanism:ANR.
What's Included in ANR Monitoring Setup
- Integration of ANR detector (Firebase Crashlytics, Sentry, or both)
- Configuration of
ApplicationExitInforeading on API 30+ for precise traces - Enabling StrictMode in debug builds for preventive diagnosis
- Configuration of velocity alerts with Slack notification
- Analysis of Android Vitals baseline for comparison with competitors
- Documentation on ANR reproduction and remediation recommendations
- Post-setup support (2 weeks free)
Our experience: more than 5 years in Android development, more than 50 monitoring projects. We guarantee an ANR rate below 0.2% after optimization. Basic setup starts at $500; full setup with Sentry and ApplicationExitInfo from $2000. This investment typically saves $10,000+ annually in reduced support costs and user churn.
How Fast Can ANR Monitoring Be Set Up?
Basic setup with Firebase Crashlytics takes from 4 hours to 1 day. If Sentry and ApplicationExitInfo analysis are required, it takes 2 days. We provide a ready dashboard and alerts. Contact us — get a detailed project assessment.
Timeline and Cost
Basic setup — from 4 hours to 1 day, starting at $500. With ApplicationExitInfo analysis and dashboard — 2 days, from $2000. Cost is calculated individually based on project complexity and number of screens. The investment pays off through reduced user churn. Get an assessment — write to us.







