Deep Investigation of Android ANR: From Thread States to Performance Optimization

Dec 04, 2025 · Programming · 11 views · 7.8

Keywords: Android ANR | Thread Analysis | Performance Optimization

Abstract: This article delves into methods for investigating Android Application Not Responding (ANR) issues, based on thread trace file analysis. It explains the root cause of ANR—main thread blocking—and demonstrates how to interpret thread states using real trace examples, particularly focusing on the main thread's behavior in MessageQueue waiting. The article then details using DDMS for real-time monitoring, StrictMode for ANR prevention, and advanced techniques for analyzing MONITOR and SUSPENDED states. Finally, it provides code examples and best practices to help developers systematically locate and resolve ANR problems, enhancing application performance.

Introduction

Android Application Not Responding (ANR) is a common performance issue in development, typically caused by long-running operations on the main thread. When an app fails to respond to user input within 5 seconds, the system triggers an ANR dialog, severely impacting user experience. Based on Q&A data, this article deeply analyzes ANR investigation methods, combining thread trace analysis and tool usage to provide a comprehensive solution.

Root Cause of ANR and Thread Analysis

The core of ANR lies in the blocking of the main thread (event loop thread). In the provided trace example, the main thread is in a TIMED_WAIT state, waiting in android.os.MessageQueue.next for messages, indicating it is idle rather than directly blocked. However, ANR can be caused by temporary long operations, such as network requests or file I/O, which, after completion, allow the thread to recover, making the trace miss the immediate blocking point. Understanding thread states is key:

In the trace, the main thread's waiting on <0x1cd570> (a android.os.MessageQueue) shows it is waiting for the message queue, which is typically normal, but if ANR occurs, it might imply a prior blocking operation. For example, if the main thread executed Thread.sleep(10000), the trace might show a SLEEPING state, but in this case, the trace was captured after ANR, thus displaying the recovered state.

Using DDMS for Real-Time Monitoring

Dalvik Debug Monitor Service (DDMS) is a powerful tool for investigating ANR. By enabling the thread view, developers can observe all thread states in real-time. When ANR occurs, immediately refreshing the main thread view can capture the exact state during blocking. For instance, if the main thread is performing a heavy computation, DDMS might show it as RUNNABLE pointing to a specific method, like com.example.MyApp.doSomethingHeavy(). This is more direct than post-analysis of traces and helps identify temporary blocks.

Code Example: Simulating ANR Scenarios

To better understand, consider the following code example demonstrating blocking operations on the main thread:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Simulate a long operation that may cause ANR
        performBlockingOperation();
    }

    private void performBlockingOperation() {
        // Incorrect approach: perform time-consuming task on main thread
        try {
            Thread.sleep(10000); // Sleep for 10 seconds, simulating network or I/O delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // Better approach: use AsyncTask
        // new AsyncTask<Void, Void, Void>() {
        //     @Override
        //     protected Void doInBackground(Void... params) {
        //         // Perform time-consuming operation
        //         return null;
        //     }
        // }.execute();
    }
}

In this example, Thread.sleep(10000) causes the main thread to block for 10 seconds, potentially triggering ANR. If monitored with DDMS, the thread state would show as SLEEPING, aiding quick problem localization. The improved solution is to use AsyncTask or thread pools to move operations to the background.

StrictMode for ANR Prevention

StrictMode is a tool provided in Android API level 9 and above to detect accidental disk or network access on the main thread. By enabling StrictMode, developers can catch violations in logs, thus preventing ANR. Here is an example configuration:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectAll() // Detect all violations
                    .penaltyLog() // Log violations
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .penaltyLog()
                    .build());
        }
    }
}

With penaltyLog(), output can be viewed via adb logcat, e.g., StrictMode policy violation; ~duration=2014 ms: android.os.StrictMode$StrictModeDiskReadViolation. This helps identify performance bottlenecks, such as file read operations on the main thread.

Advanced Investigation Techniques: Analyzing MONITOR and SUSPENDED States

When a trace shows a MONITOR state, it may indicate deadlock issues. For example, if thread A is waiting for a lock held by thread B, and thread B is waiting for thread A, ANR can occur. In the trace, look for "waiting to lock" statements, such as:

"Thread-1" prio=5 tid=10 MONITOR
  | waiting to lock <0x1a2b3c> (a com.example.MyClass) held by tid=9

This indicates that thread tid=10 is waiting for a lock held by tid=9. Further check the state of tid=9; if it is SUSPENDED, it might mean the thread is suspended, causing deadlock. In such cases, review synchronization blocks or lock mechanisms in the code. For example:

public class DeadlockExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            // Simulate time-consuming operation
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            synchronized (lock2) { // May wait for lock2
                // Code logic
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            synchronized (lock1) { // May wait for lock1
                // Code logic
            }
        }
    }
}

If two threads execute method1 and method2 simultaneously, deadlock may occur. Using DDMS or analyzing traces can reveal this state.

Best Practices and Conclusion

Investigating ANR requires a systematic approach: first, check for long operations in code, ensuring they are not executed on the main thread; second, utilize DDMS for real-time monitoring; then, use StrictMode to prevent issues; finally, deeply analyze thread states in traces. For complex cases, consider profiling tools like Android Profiler. By combining these techniques, developers can effectively reduce ANR incidence and improve application responsiveness. Remember, ANR is not only a technical issue but also impacts user experience, making continuous optimization essential.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.