ANR: Prevention and Diagnosis
ANR (Application Not Responding) is the ultimate signal of a critical failure in user experience. It occurs when the Android system detects that the application's Main Thread has been unresponsive for a predefined duration, triggering a system dialog that allows the user to force-close the app.
1. System Timeout Thresholds
The system monitors different component types with specific timers. Exceding these triggers an ANR:
- Input Events: An activity fails to respond to a touch or key event within 5 seconds.
- Services: A foreground service fails to complete its lifecycle method (
onCreate,onStartCommand) within 20 seconds (200 seconds for background services). - BroadcastReceivers: The
onReceive()method fails to complete in 10 seconds for foreground broadcasts or 60 seconds for background ones.
2. Fundamental Causes of ANR
An ANR is almost always the result of a "Stalled Main Thread." Common culprits include:
- Blocking I/O: Executing network requests, database transactions, or large file reads directly on the UI thread.
- Lock Contention: The Main Thread is blocked waiting for a
synchronizedlock held by a long-running background thread. Even if the Main thread's own logic is lightweight, it is held hostage by the lock. - Computational Saturation: Performing heavy mathematical operations, image processing, or complex parsing directly on the UI thread.
- Message Queue Congestion: A high volume of rapid UI updates or complex
onBindViewHolderlogic in aRecyclerView. While each individual message may be fast, the sheer volume prevents the "input event" message from being processed in time.
3. Investigating ANR via traces.txt
When an ANR occurs, Android dumps the execution states of all threads into a trace file (typically located in /data/anr/).
- The Main Thread Analysis: Look for the thread named
"main". - Critical State Indicators:
Blocked: Indicates the thread is stuck waiting for a monitor/lock. Identify which background thread holds the lock and why it hasn't released it.Sleeping: Indicates a manual pause (e.g.,Thread.sleep()) which should never occur on the Main thread.Native: Indicates the thread is executing C++ code; check if JNI calls are blocking for too long.
4. Proactive Identification: StrictMode
During the development phase, StrictMode is the primary tool for surfacing potential ANR issues before they reach production. It allows you to set "Policies" that trigger warnings or crashes when the Main thread perform disk or network I/O.
// Implementation in Application.onCreate for Debug builds
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog() // Log the violation
.penaltyDropBox() // Record to system dropbox for audit
.build()
)
}
5. Architectural Fix: Main-Safe Execution
The industry-standard solution is the Main-Safe pattern using Kotlin Coroutines. By offloading I/O and heavy work to Dispatchers.IO or Dispatchers.Default using withContext, you ensure the Main thread is always available to handle input events and frame rendering.
class UserRepository(private val api: UserApi) {
// This function is "Main-Safe" - it can be called from any thread
suspend fun getUserProfile(id: String) = withContext(Dispatchers.IO) {
api.fetchProfile(id) // Network I/O occurs on a background thread
}
}