DEV Community

WonderLab
WonderLab

Posted on

Deep Analysis of ANR Mechanism: From Trigger to Reporting

Introduction: The ANR Dialog We Love and Hate

Remember the first time you saw an ANR Dialog?

"Application XX is not responding. Do you want to close it?"

As a user, this dialog is frustrating—the app is frozen, nothing works. But as a developer, this dialog is a lifesaver—at least it tells you something is wrong, and it leaves you traces.txt as the "crime scene."

I once encountered a particularly troublesome ANR: when users switched WiFi in the Settings interface, the entire system UI froze. Looking at logcat, I found the main thread waiting for a lock; examining traces.txt, I discovered the lock-holding thread was making a Binder call; further investigation revealed that excessive ContentObserver registrations led to Binder resource exhaustion... It's like a detective game, and the ANR Dialog is the starting point of the case.

This article will take you deep into the internals of the ANR mechanism, from Input event dispatching to timeout detection, from trace information collection to Dialog popup, providing a complete analysis of ANR's full lifecycle. After reading this article, you will be able to:

  1. Understand the 4 types of ANR and their timeout durations
  2. Master how InputDispatcher detects Input ANR
  3. Understand how ActivityManagerService handles ANR
  4. Learn to analyze traces.txt and find root causes
  5. Build ANR monitoring and prevention mechanisms

Ready? Let's unveil the mystery of the ANR mechanism!


I. ANR Fundamentals

1.1 What is ANR?

ANR (Application Not Responding) is a protection mechanism in the Android system. When an application fails to respond to user operations or system events within a specified time, the system considers the app "dead" and pops up an ANR dialog, allowing users to choose whether to continue waiting or force close.

To draw an analogy: ANR is like a restaurant's "order timeout reminder." You order a dish, and if the kitchen hasn't served it within 5 minutes, the waiter comes over to ask "Would you like to keep waiting, or order something else?" This prevents you from waiting indefinitely.

Core Functions of ANR:

  • Protect User Experience: Prevent applications from being unresponsive for extended periods
  • Timely Problem Detection: Record the "crime scene" through traces.txt
  • System Self-Protection: Prevent a single application from dragging down the entire system

1.2 The 4 Types of ANR

The Android system sets different timeout durations for different types of events:

ANR Type Timeout Trigger Scenario Detection Location
Input ANR 5 seconds Touch/key events not processed InputDispatcher
Broadcast ANR 10s foreground / 60s background BroadcastReceiver's onReceive() not completed ActivityManagerService
Service ANR 20s foreground / 200s background Service lifecycle methods not completed ActivityManagerService
ContentProvider ANR 10 seconds ContentProvider publish timeout ActivityManagerService

Why are the timeout durations different?

This is based on a trade-off between user experience and system responsiveness:

  • Input ANR is shortest (5 seconds): User operations must receive quick feedback, otherwise users will think the device is broken
  • Broadcast ANR is moderate (10s/60s): Broadcast receivers should execute quickly but are allowed some processing time
  • Service ANR is longer (20s/200s): Background services can handle time-consuming operations, given more tolerance
  • Background process timeout is even longer: Background operations don't affect foreground experience, can wait longer

1.3 ANR vs Freeze vs Watchdog

These concepts are often confused. Let's compare them:

Feature ANR Freeze Watchdog
Detection Target Application process Application process System Server
Detection Dimension Specific event response Overall responsiveness Core thread health
Timeout Duration 5-200 seconds Usually less than a second 30-60 seconds
Consequence ANR Dialog popup May trigger ANR System restart
Scope Single application Single application Entire system

Vivid Analogy:

  • ANR: Restaurant waiter checking if your ordered dishes are served
  • Freeze: Restaurant manager monitoring if waiters are working
  • Watchdog: Fire department periodically inspecting if the restaurant's fire safety system is functioning properly

II. Complete Flow of Input ANR

Input ANR is the most common and typical type of ANR. Let's walk through the complete ANR detection flow using a scenario where a user clicks a button.

2.1 Overall Flow Overview

02-01-anr-input-flow

2.2 InputReader: The Source of Events

InputReader runs in an independent thread, responsible for reading input events from the kernel:

// frameworks/native/services/inputflinger/reader/InputReader.cpp

void InputReader::loopOnce() {
    // 1. Read raw events from /dev/input/eventX
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    // 2. Process each event
    for (size_t i = 0; i < count; i++) {
        const RawEvent& rawEvent = mEventBuffer[i];

        // 3. Convert to Android events based on device type (touchscreen, keyboard, etc.)
        processEventsLocked(rawEvent);
    }

    // 4. Hand processed events to InputDispatcher
    mQueuedListener->flush();
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • InputReader is the "porter" of events, only responsible for reading and preliminary processing
  • It doesn't care whether events are processed by applications, just forwards them
  • The actual timeout detection happens in InputDispatcher

2.3 InputDispatcher: The Heart of Timeout Detection

InputDispatcher is the "heart" of ANR detection. Let's see how it detects timeouts:

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

// Core logic for dispatching events
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;

    // 1. Dequeue pending events from the queue
    if (mPendingEvent == nullptr) {
        mPendingEvent = mInboundQueue.dequeueAtHead();
    }

    // 2. Find the target Window for the event
    std::vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(
            currentTime, mPendingEvent, inputTargets, nextWakeupTime);

    // 3. Dispatch event to target Window
    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
        dispatchEventLocked(currentTime, mPendingEvent, inputTargets);
    }

    // 4. Check for connection timeouts
    nsecs_t nextAnrCheck = checkUnresponsiveConnectionsLocked(currentTime);
    if (nextAnrCheck < nextWakeupTime) {
        nextWakeupTime = nextAnrCheck;
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Logic for Timeout Detection:

// Check if connection is responsive
nsecs_t InputDispatcher::checkUnresponsiveConnectionsLocked(nsecs_t currentTime) {
    nsecs_t nextAnrCheck = LONG_LONG_MAX;

    for (const auto& [token, connection] : mConnectionsByToken) {
        // Check this connection's wait queue
        if (connection->waitQueue.empty()) {
            continue;
        }

        // Get head event
        DispatchEntry* head = connection->waitQueue.head;
        nsecs_t eventTime = head->eventEntry->eventTime;

        // Calculate wait duration
        nsecs_t waitDuration = currentTime - eventTime;

        // If exceeds 5 seconds (5000ms), trigger ANR
        if (waitDuration > 5000ms) {
            onAnrLocked(connection);
        } else {
            // Record next check time
            nsecs_t timeoutTime = eventTime + 5000ms;
            if (timeoutTime < nextAnrCheck) {
                nextAnrCheck = timeoutTime;
            }
        }
    }

    return nextAnrCheck;
}
Enter fullscreen mode Exit fullscreen mode

Elegant Design:

  1. Record timestamp when event enters queue: Each event dispatch records eventTime
  2. Periodically check head event: InputDispatcher periodically checks the head of waitQueue
  3. Calculate wait duration: waitDuration = currentTime - eventTime
  4. Timeout triggers ANR: If wait exceeds 5 seconds, call onAnrLocked()

Why check the head instead of the tail?

Because the head is the earliest sent event. If even the head hasn't been processed, it means the app is truly stuck. It's like queuing for tickets: if the person at the front is still being served, those behind naturally can't proceed.

2.4 The Secret of Wait Queue (waitQueue)

Understanding waitQueue is key to understanding Input ANR:

Lifecycle of waitQueue:

1. Before event is sent
   waitQueue: []

2. Event sent to application
   dispatchEntry enqueued
   waitQueue: [Event1]
   Record Event1.eventTime = T0

3. Application processes event (normal case)
   Application calls finish()
   waitQueue: []  ← Event1 removed

4. Application stuck (abnormal case)
   After T0 + 5 seconds
   waitQueue: [Event1]  ← Event1 still there!
   InputDispatcher detects timeout
   Triggers ANR
Enter fullscreen mode Exit fullscreen mode

Code Implementation:

// Called after application finishes processing event
void InputDispatcher::finishDispatchCycleLocked(
        nsecs_t currentTime, sp<Connection> connection) {
    // Remove processed events from waitQueue
    while (!connection->waitQueue.empty()) {
        DispatchEntry* dispatchEntry = connection->waitQueue.head;
        connection->waitQueue.dequeue(dispatchEntry);

        // Release resources
        releaseDispatchEntry(dispatchEntry);

        // If this is the last event, reset timeout check
        if (connection->waitQueue.empty()) {
            connection->lastEventTime = LLONG_MIN;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2.5 ANR Analysis Example

Let's look at a simplified analysis of a real Input ANR scenario:

Scenario: User quickly swipes in a list interface, suddenly the app becomes unresponsive

logcat Log:

12-25 10:30:15.123  1234  1250 E InputDispatcher: channel 'e8d3a72 com.example.app/com.example.app.MainActivity (server)' ~ Channel is unresponsive! Waited 5008ms
12-25 10:30:15.234  1234  1250 I InputDispatcher: Delivering ANR to com.example.app
Enter fullscreen mode Exit fullscreen mode

traces.txt Fragment:

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x74e04dd8 self=0x7a4e014c00
  | sysTid=12345 nice=-10 cgrp=default sched=0/0 handle=0x7b5a9f49a8
  | state=S schedstat=( 15678923456 8765432109 1234 ) utm=1500 stm=67 core=2 HZ=100
  at com.example.app.RecyclerAdapter.onBindViewHolder(RecyclerAdapter.java:150)
  - waiting to lock <0x0a1b2c3d> (a java.lang.Object) held by thread 15
  at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3500)
  at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:4064)
  at android.view.View.layout(View.java:19839)
  ...
Enter fullscreen mode Exit fullscreen mode

Root Cause Analysis:

  1. Phenomenon: Main thread is blocked, state is Blocked
  2. Location: Stuck at RecyclerAdapter.onBindViewHolder()
  3. Cause: Main thread waiting for a lock <0x0a1b2c3d>, held by thread 15
  4. Solution: Find what thread 15 is doing, resolve the deadlock

Key Lessons:

  • Never wait for locks on the main thread
  • Don't perform time-consuming operations in RecyclerView's Adapter
  • Complex data processing should be done in background threads

III. Broadcast ANR Detection Mechanism

Compared to Input ANR's "passive detection" (waiting for event processing to complete), Broadcast ANR uses "active detection" (actively setting timeout).

3.1 Broadcast Distribution Flow

02-02-broadcast-anr-flow

3.2 Timeout Timer Implementation

// frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

private void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app) {
    // 1. Set timeout duration
    final long timeout = r.isForeground() ?
            BROADCAST_FG_TIMEOUT : BROADCAST_BG_TIMEOUT;

    // 2. Send delayed message
    mHandler.sendMessageDelayed(
            mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, r),
            timeout);

    // 3. Call receiver's onReceive()
    app.thread.scheduleReceiver(...);
}

// Handler processes timeout message
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case BROADCAST_TIMEOUT_MSG:
            BroadcastRecord r = (BroadcastRecord) msg.obj;
            // Trigger ANR
            broadcastTimeoutLocked(r, true);
            break;
    }
}

// Cancel timeout after broadcast execution completes
private void performReceiveLocked(ProcessRecord app, ...) {
    // 1. Execution complete
    r.receiver = null;

    // 2. Cancel timeout message
    mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, r);

    // 3. Continue processing next broadcast
    processCurBroadcastLocked(r, app);
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • Use Handler's delayed messages to implement timeout detection
  • If completed before timeout, removeMessages() cancels the timer
  • If timeout occurs, Handler receives the message and triggers ANR

3.3 Foreground vs Background Broadcast Differences

// Timeout duration definitions
static final int BROADCAST_FG_TIMEOUT = 10 * 1000;  // 10 seconds
static final int BROADCAST_BG_TIMEOUT = 60 * 1000;  // 60 seconds

// How to determine foreground or background?
boolean isForeground() {
    // 1. Broadcast sent with FLAG_RECEIVER_FOREGROUND specified
    if ((intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0) {
        return true;
    }

    // 2. Receiving process is a foreground process
    if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
        return true;
    }

    // 3. Current receiver of ordered broadcast is foreground
    if (ordered && curReceiver.priority > 0) {
        return true;
    }

    return false;
}
Enter fullscreen mode Exit fullscreen mode

Practical Case:

// Case: A time-consuming BroadcastReceiver
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // ❌ Wrong: Doing time-consuming work on main thread
        for (int i = 0; i < 1000000; i++) {
            // Heavy computation
        }
        // If exceeds 10 seconds, triggers ANR
    }
}

// ✅ Correct approach: Use goAsync()
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 1. Get PendingResult
        final PendingResult result = goAsync();

        // 2. Process in background thread
        new Thread(() -> {
            try {
                // Time-consuming operation
                doHeavyWork();
            } finally {
                // 3. Notify system when complete
                result.finish();
            }
        }).start();
    }
}
Enter fullscreen mode Exit fullscreen mode

What goAsync() does:

  • Tells the system "I need async processing, don't rush the timeout"
  • System extends timeout duration (but still has limits)
  • Must call result.finish() when complete

IV. Service ANR Detection Mechanism

Service ANR detection logic is similar to Broadcast but more complex, as Services have multiple lifecycle methods that need monitoring.

4.1 Timeout Monitoring of Service Lifecycle

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

// Service start timeout detection
private final void bringUpServiceLocked(ServiceRecord r, ...) {
    // 1. Set timeout duration
    final long timeout = r.isForeground() ?
            SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT;

    // 2. Send timeout message
    mAm.mHandler.sendMessageDelayed(
            mAm.mHandler.obtainMessage(SERVICE_TIMEOUT_MSG, r),
            timeout);

    // 3. Start Service
    app.thread.scheduleCreateService(r, ...);
}

// Service lifecycle method execution complete
private void serviceDoneExecutingLocked(ServiceRecord r, ...) {
    // 1. Cancel timeout message
    mAm.mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r);

    // 2. Update Service state
    r.executeNesting--;
    if (r.executeNesting <= 0) {
        r.executing = false;
    }
}
Enter fullscreen mode Exit fullscreen mode

Monitored Lifecycle Methods:

  • onCreate(): Service creation
  • onStartCommand(): Service start
  • onBind(): Service binding
  • onUnbind(): Service unbinding
  • onDestroy(): Service destruction

4.2 Special Handling of Foreground Services

// Foreground Service timeout is shorter
static final int SERVICE_TIMEOUT = 20 * 1000;       // 20 seconds
static final int SERVICE_BACKGROUND_TIMEOUT = 200 * 1000;  // 200 seconds

// Correct way to start foreground Service
public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();

        // Call startForeground() as soon as possible in onCreate()
        // Otherwise may trigger ANR
        Notification notification = createNotification();
        startForeground(NOTIFICATION_ID, notification);

        // Then do other initialization
        initializeResources();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // If there are time-consuming operations, put them in background thread
        new Thread(() -> {
            doHeavyWork();
        }).start();

        return START_STICKY;
    }
}
Enter fullscreen mode Exit fullscreen mode

Why is foreground Service timeout shorter?

Because foreground Services are usually related to user's current operations (like music playback, navigation), they must start quickly, otherwise user experience is affected.

4.3 IntentService Timeout Issues

IntentService is a special Service that comes with its own worker thread:

// IntentService internal implementation
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        // Create worker thread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Send task to worker thread
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
        return START_REDELIVER_INTENT;
    }

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            // Call onHandleIntent() on worker thread
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    // Subclasses implement this method
    protected abstract void onHandleIntent(Intent intent);
}
Enter fullscreen mode Exit fullscreen mode

Important Details:

  • onStartCommand() is on main thread but returns quickly
  • onHandleIntent() is on worker thread, can be time-consuming
  • Won't trigger Service ANR because main thread methods are fast

But watch out for onCreate()!

// ❌ Wrong: Initialize time-consuming resources in onCreate()
public class MyIntentService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        // This is still on main thread!
        loadLargeDatabase();  // May trigger ANR
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // This is on worker thread, can be time-consuming
        processData();
    }
}

// ✅ Correct: Put time-consuming initialization in worker thread too
public class MyIntentService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        // Only do lightweight initialization
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Initialize on first call
        if (!initialized) {
            loadLargeDatabase();
            initialized = true;
        }
        processData();
    }
}
Enter fullscreen mode Exit fullscreen mode

V. ANR Information Collection and Reporting

When InputDispatcher or AMS detects an ANR, it triggers a series of information collection operations. This information is crucial for analyzing ANR.

5.1 ANR Trigger and Information Collection Flow

02-03-anr-collection-flow

5.2 Implementation of appNotResponding()

// frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java

public void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
        String parentShortComponentName, WindowProcessController parentProcess,
        boolean aboveSystem, String annotation) {

    // 1. Prevent duplicate ANR
    synchronized (mService) {
        if (mNotResponding) {
            Slog.w(TAG, "App already notresponding: " + this);
            return;
        }
        mNotResponding = true;

        // 2. Record ANR information
        mNotRespondingReport = generateErrorReport(
                annotation, null, null, null, null, null);
    }

    // 3. Collect CPU usage
    updateCpuStatsNow();

    // 4. Output to logcat
    Slog.e(TAG, "ANR in " + processName + " (" + activityShortComponentName + ")");
    Slog.e(TAG, "PID: " + pid);
    Slog.e(TAG, "Reason: " + annotation);
    Slog.e(TAG, "Load: " + loadAverage);
    Slog.e(TAG, "CPU usage from " + cpuUsageStart + "ms to " + cpuUsageEnd + "ms later:");
    Slog.e(TAG, cpuUsageString);

    // 5. Dump all related thread stacks
    ArrayList<Integer> firstPids = new ArrayList<>(5);
    firstPids.add(pid);  // ANR process
    firstPids.add(Process.myPid());  // System Server

    File tracesFile = ActivityManagerService.dumpStackTraces(
            firstPids, processCpuTracker, ...);

    // 6. Write to DropBox
    mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
            parentShortComponentName, parentProcess, annotation,
            cpuUsageString, tracesFile, null);

    // 7. Pop up ANR Dialog
    Message msg = Message.obtain();
    msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
    msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
    mService.mUiHandler.sendMessage(msg);
}
Enter fullscreen mode Exit fullscreen mode

5.3 Generation of traces.txt

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public static File dumpStackTraces(ArrayList<Integer> firstPids,
        ProcessCpuTracker processCpuTracker, ...) {

    // 1. Create traces file
    File tracesFile = new File("/data/anr/traces.txt");

    // 2. Dump first batch of processes (ANR process and System Server)
    for (int pid : firstPids) {
        dumpJavaTracesTombstoned(pid, tracesFile, timeout);
    }

    // 3. Dump Native processes (if ANR process has native threads)
    if (DEBUG_ANR) {
        dumpNativeBacktraceToFile(pid, tracesFile);
    }

    // 4. Dump other important processes
    ArrayList<Integer> extraPids = new ArrayList<>();

    // 4.1 Last 3 recently used applications
    if (lastPids != null) {
        for (int i = 0; i < lastPids.size() && i < 3; i++) {
            extraPids.add(lastPids.get(i));
        }
    }

    // 4.2 All persistent processes
    synchronized (mPidsSelfLocked) {
        for (int i = 0; i < mPidsSelfLocked.size(); i++) {
            ProcessRecord r = mPidsSelfLocked.valueAt(i);
            if (r.persistent) {
                extraPids.add(r.pid);
            }
        }
    }

    // 4.3 Dump these processes
    for (int pid : extraPids) {
        dumpJavaTracesTombstoned(pid, tracesFile, timeout);
    }

    return tracesFile;
}
Enter fullscreen mode Exit fullscreen mode

Structure of traces.txt:

----- pid 12345 at 2024-12-25 10:30:15 -----
Cmd line: com.example.app
Build fingerprint: 'google/sdk_gphone_x86_64/generic_x86_64:11/RSR1.201013.001/6903271:user/release-keys'
ABI: 'x86_64'
...

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x74e04dd8
  | sysTid=12345 nice=-10 cgrp=default sched=0/0 handle=0x7b5a9f49a8
  | state=S schedstat=( 15678923456 8765432109 1234 ) utm=1500 stm=67
  at com.example.app.MainActivity.onClick(MainActivity.java:150)
  - waiting to lock <0x0a1b2c3d> (a java.lang.Object) held by thread 15
  at android.view.View.performClick(View.java:7125)
  ...

"Thread-15" prio=5 tid=15 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x13579bdf
  | sysTid=12360 nice=0 cgrp=default sched=0/0 handle=0x7a4e028800
  at android.os.BinderProxy.transactNative(Native Method)
  - locked <0x0a1b2c3d> (a java.lang.Object)
  at android.os.BinderProxy.transact(Binder.java:1129)
  ...

"Binder:12345_2" prio=5 tid=12 Native
  ...

----- end 12345 -----
Enter fullscreen mode Exit fullscreen mode

Key Information Interpretation:

  • Cmd line: Process name
  • tid: Thread ID
  • State: Blocked/Native/Waiting/Runnable
  • Stack: Java method call chain
  • Lock information: waiting to lock <address> or locked <address>

5.4 DropBox: ANR's "Black Box"

DropBox is Android's system logging service, specifically for storing system exception events:

// Write to DropBox
void addErrorToDropBox(String eventType, ProcessRecord process,
        String processName, String activityShortComponentName, ...) {

    // 1. Build DropBox entry
    StringBuilder sb = new StringBuilder(1024);
    sb.append("Process: ").append(processName).append("\n");
    sb.append("PID: ").append(process.pid).append("\n");
    sb.append("Reason: ").append(annotation).append("\n");
    sb.append("Load: ").append(loadAverage).append("\n");
    sb.append(cpuUsageString);

    // 2. Add traces content
    if (tracesFile != null) {
        sb.append("\n\n*** TRACES ***\n");
        sb.append(FileUtils.readTextFile(tracesFile, DROPBOX_MAX_SIZE, "..."));
    }

    // 3. Write to DropBox
    DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
    dbox.addText(eventType, sb.toString());
}
Enter fullscreen mode Exit fullscreen mode

View DropBox Content:

# List all ANR records
adb shell dumpsys dropbox --print anr

# Export recent ANR
adb shell dumpsys dropbox --print anr > anr_dropbox.txt

# Clear DropBox
adb shell dumpsys dropbox --wipe
Enter fullscreen mode Exit fullscreen mode

DropBox vs traces.txt:
| Feature | DropBox | traces.txt |
|---------|---------|------------|
| Storage Location | /data/system/dropbox | /data/anr/ |
| Storage Format | Structured entries | Plain text |
| Capacity Limit | Limited (rolling deletion) | Single file |
| Information Included | ANR + CPU + Memory | Thread stacks only |
| View Method | dumpsys dropbox | adb pull |


VI. ANR Dialog Display

After ANR detection and information collection complete, a dialog is finally popped up for users to decide how to handle it.

6.1 Types of ANR Dialog

Android has two types of ANR Dialog:

1. Application ANR Dialog (regular applications):

─────────────────────────────
│  Application Not Responding │
│                            │
│  "XX" is not responding.   │
│                            │
│  Do you want to close it?  │
│                            │
│  [Wait]  [Close App]       │
─────────────────────────────
Enter fullscreen mode Exit fullscreen mode

2. System ANR Dialog (system applications or System Server):

─────────────────────────────
│  System Not Responding     │
│                            │
│  "System UI" is not        │
│  responding.               │
│                            │
│  [Wait]  [OK]              │
─────────────────────────────
Enter fullscreen mode Exit fullscreen mode

6.2 Dialog Implementation

// frameworks/base/services/core/java/com/android/server/am/AppNotRespondingDialog.java

final class AppNotRespondingDialog extends BaseErrorDialog {
    private final ActivityManagerService mService;
    private final ProcessRecord mProc;

    public AppNotRespondingDialog(ActivityManagerService service,
            Context context, Data data) {
        super(context);

        mService = service;
        mProc = data.proc;

        // 1. Set Dialog title
        setTitle(context.getText(com.android.internal.R.string.anr_title));

        // 2. Set message content
        StringBuilder msg = new StringBuilder();
        msg.append(context.getString(com.android.internal.R.string.anr_activity_application,
                data.aInfo.loadLabel(context.getPackageManager()).toString(),
                data.proc.info.processName));

        setMessage(msg);

        // 3. Set buttons
        setCancelable(false);

        // "Wait" button
        setButton(DialogInterface.BUTTON_POSITIVE,
                context.getText(com.android.internal.R.string.wait),
                mHandler.obtainMessage(WAIT));

        // "Close App" button
        setButton(DialogInterface.BUTTON_NEGATIVE,
                context.getText(com.android.internal.R.string.force_close),
                mHandler.obtainMessage(FORCE_CLOSE));

        // "Report" button (optional)
        if (data.proc.errorReportReceiver != null) {
            setButton(DialogInterface.BUTTON_NEUTRAL,
                    context.getText(com.android.internal.R.string.report),
                    mHandler.obtainMessage(APP_INFO));
        }
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case WAIT:
                    // User chooses to wait, close Dialog
                    mService.mUiHandler.sendEmptyMessage(DISMISS_DIALOG);
                    break;

                case FORCE_CLOSE:
                    // User chooses to close, kill application
                    mService.killAppAtUsersRequest(mProc, AppNotRespondingDialog.this);
                    break;

                case APP_INFO:
                    // User chooses to report, open app info page
                    mService.mUiHandler.obtainMessage(SHOW_APP_ERROR_REPORT, mProc)
                            .sendToTarget();
                    break;
            }
        }
    };
}
Enter fullscreen mode Exit fullscreen mode

6.3 Follow-up Handling of User Actions

User Chooses "Wait":

// Do nothing, just close Dialog
// ANR process continues running
// If still unresponsive, may trigger ANR again
Enter fullscreen mode Exit fullscreen mode

User Chooses "Close App":

void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
    synchronized (this) {
        // 1. Mark as user-initiated close
        app.crashing = false;
        app.crashingReport = null;
        app.notResponding = false;
        app.notRespondingReport = null;

        // 2. Kill process
        Process.killProcess(app.pid);

        // 3. Clean up process record
        handleAppDiedLocked(app, false, true);
    }
}
Enter fullscreen mode Exit fullscreen mode

User Chooses "Report" (some systems):

// Open Feedback or BugReport application
Intent intent = new Intent("android.intent.action.APP_ERROR");
intent.putExtra("error_type", "anr");
intent.putExtra("package_name", app.info.packageName);
intent.putExtra("process_name", app.processName);
intent.putExtra("pid", app.pid);
context.startActivity(intent);
Enter fullscreen mode Exit fullscreen mode

VII. ANR Analysis in Practice

We've covered a lot of theory. Let's see how to analyze ANR in practice through several real cases.

7.1 Case 1: ANR Caused by Main Thread Waiting for Lock

Phenomenon: After user clicks button, app freezes for 5 seconds, ANR pops up

logcat Log:

12-25 10:30:15.123  1234  1250 E InputDispatcher: Application is not responding: AppWindowToken{e8d3a72 token=Token{b6c7d3e ActivityRecord{a9f8e1d u0 com.example/.MainActivity}}}
12-25 10:30:15.234  1234  1250 I ActivityManager: ANR in com.example.app (com.example.app/.MainActivity)
12-25 10:30:15.234  1234  1250 I ActivityManager: PID: 12345
12-25 10:30:15.234  1234  1250 I ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 1.)
Enter fullscreen mode Exit fullscreen mode

traces.txt Analysis:

----- pid 12345 at 2024-12-25 10:30:15 -----
Cmd line: com.example.app

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x74e04dd8
  | sysTid=12345 nice=-10 cgrp=default sched=0/0 handle=0x7b5a9f49a8
  | state=S schedstat=( 15678923456 8765432109 1234 ) utm=1500 stm=67
  at com.example.app.DataManager.getData(DataManager.java:45)
  - waiting to lock <0x0a1b2c3d> (a java.lang.Object) held by thread 15
  at com.example.app.MainActivity.onClick(MainActivity.java:100)
  at android.view.View.performClick(View.java:7125)
  at android.view.View.performClickInternal(View.java:7102)
  at android.view.View.access$3500(View.java:801)
  at android.view.View$PerformClick.run(View.java:27851)
  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:7356)
  ...

"Thread-15" prio=5 tid=15 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x13579bdf
  | sysTid=12360 nice=0 cgrp=default sched=0/0 handle=0x7a4e028800
  | state=S schedstat=( 9876543210 5432109876 123 ) utm=900 stm=87
  at android.os.BinderProxy.transactNative(Native Method)
  at android.os.BinderProxy.transact(Binder.java:1129)
  - locked <0x0a1b2c3d> (a java.lang.Object)
  at android.content.IContentProvider$Stub$Proxy.query(IContentProvider.java:xxx)
  at android.content.ContentResolver.query(ContentResolver.java:xxx)
  at com.example.app.DataManager.loadFromDatabase(DataManager.java:80)
  at com.example.app.DataManager$1.run(DataManager.java:60)
  ...
Enter fullscreen mode Exit fullscreen mode

Analysis Steps:

  1. Check main thread state: Blocked - main thread is blocked
  2. Check blocking location: DataManager.getData() line 45, waiting for a lock <0x0a1b2c3d>
  3. Find lock-holding thread: Thread-15 holds this lock (locked <0x0a1b2c3d>)
  4. See what lock-holding thread is doing: Making Binder call transactNative(), querying ContentProvider
  5. Root cause: Thread 15 holds lock while doing time-consuming operation, main thread can't get lock

Solution:

// ❌ Original code
public class DataManager {
    private final Object mLock = new Object();

    public void onClick() {
        // Called from main thread
        synchronized (mLock) {
            Data data = getData();
            updateUI(data);
        }
    }

    private Data getData() {
        // Still holding lock!
        return loadFromDatabase();  // Binder call, very slow
    }
}

// ✅ After fix
public class DataManager {
    private final Object mLock = new Object();

    public void onClick() {
        // Async load
        new Thread(() -> {
            Data data;
            synchronized (mLock) {
                data = getData();
            }
            // Update UI on main thread
            runOnUiThread(() -> updateUI(data));
        }).start();
    }
}
Enter fullscreen mode Exit fullscreen mode

7.2 Case 2: ANR Caused by Binder Call Timeout

Phenomenon: App in background, suddenly receives Broadcast ANR

logcat:

12-25 11:00:00.123  1234  1250 W BroadcastQueue: Timeout of broadcast BroadcastRecord{a1b2c3d u0 android.intent.action.BATTERY_CHANGED} - receiver=android.content.IIntentReceiver$Stub$Proxy@e4f5g6h
12-25 11:00:00.234  1234  1250 I ActivityManager: ANR in com.example.app (com.example.app/.MyReceiver)
12-25 11:00:00.234  1234  1250 I ActivityManager: Reason: Broadcast of Intent { act=android.intent.action.BATTERY_CHANGED }
Enter fullscreen mode Exit fullscreen mode

traces.txt:

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x74e04dd8
  | sysTid=23456 nice=0 cgrp=default sched=0/0 handle=0x7b5a9f49a8
  | state=S schedstat=( 2345678901 1234567890 234 ) utm=200 stm=34
  at android.os.BinderProxy.transactNative(Native Method)
  at android.os.BinderProxy.transact(Binder.java:1129)
  at android.app.IActivityManager$Stub$Proxy.registerContentObserver(IActivityManager.java:xxxx)
  at android.content.ContentResolver.registerContentObserver(ContentResolver.java:xxxx)
  at com.example.app.MyReceiver.onReceive(MyReceiver.java:30)
  at android.app.ActivityThread.handleReceiver(ActivityThread.java:xxxx)
  ...
Enter fullscreen mode Exit fullscreen mode

Analysis:

  1. ANR Type: Broadcast ANR
  2. Main thread state: Native - executing Native method
  3. Stuck where: BinderProxy.transactNative() - Binder call
  4. Doing what: Registering ContentObserver
  5. Why slow: Possibly System Server busy or Binder resources exhausted

Root Cause: Synchronous Binder call in BroadcastReceiver, while System Server responds slowly

Solution:

// ❌ Original code
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Synchronously register ContentObserver, may be slow
        context.getContentResolver().registerContentObserver(
                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
                false,
                new ContentObserver(null) {
                    @Override
                    public void onChange(boolean selfChange) {
                        // ...
                    }
                });
    }
}

// ✅ Fix Option 1: Use goAsync()
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final PendingResult result = goAsync();

        new Thread(() -> {
            try {
                context.getContentResolver().registerContentObserver(...);
            } finally {
                result.finish();
            }
        }).start();
    }
}

// ✅ Fix Option 2: Delayed registration
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Start Service for delayed registration
        Intent serviceIntent = new Intent(context, RegistrationService.class);
        context.startService(serviceIntent);
    }
}
Enter fullscreen mode Exit fullscreen mode

7.3 Case 3: Database Operations on Main Thread

traces.txt:

"main" prio=5 tid=1 Native
  | sysTid=34567 nice=-10
  at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
  at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:609)
  at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:820)
  at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
  at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:144)
  at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:134)
  at com.example.app.DatabaseHelper.queryAllRecords(DatabaseHelper.java:150)
  at com.example.app.MainActivity.onCreate(MainActivity.java:50)
  ...
Enter fullscreen mode Exit fullscreen mode

Analysis:

  • Main thread executing SQLite query
  • getCount() loads all data into Cursor
  • Very slow with large datasets

Solution:

// ❌ Original code
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Query database on main thread
    Cursor cursor = mDbHelper.queryAllRecords();
    int count = cursor.getCount();  // Triggers actual query
    processData(cursor);
}

// ✅ Using AsyncTask (deprecated, example only)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new QueryTask().execute();
}

private class QueryTask extends AsyncTask<Void, Void, Cursor> {
    @Override
    protected Cursor doInBackground(Void... voids) {
        return mDbHelper.queryAllRecords();
    }

    @Override
    protected void onPostExecute(Cursor cursor) {
        processData(cursor);
    }
}

// ✅ Using Kotlin Coroutines (recommended)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    lifecycleScope.launch {
        val cursor = withContext(Dispatchers.IO) {
            dbHelper.queryAllRecords()
        }
        processData(cursor)
    }
}

// ✅ Using Room + LiveData (best practice)
@Dao
interface RecordDao {
    @Query("SELECT * FROM records")
    fun getAllRecords(): LiveData<List<Record>>
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    viewModel.records.observe(this) { records ->
        processData(records)
    }
}
Enter fullscreen mode Exit fullscreen mode

VIII. ANR Prevention and Monitoring

Prevention is better than cure. Let's see how to avoid ANR during development and how to build an effective monitoring system.

8.1 Prevention Measures During Development

1. Use StrictMode to Detect Main Thread Violations

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()    // Detect disk reads
                    .detectDiskWrites()   // Detect disk writes
                    .detectNetwork()      // Detect network operations
                    .detectCustomSlowCalls()  // Detect custom slow calls
                    .penaltyLog()         // Output to logcat
                    .penaltyDeath()       // Direct crash (development phase)
                    .build());

            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()  // Detect database leaks
                    .detectLeakedClosableObjects() // Detect unclosed objects
                    .penaltyLog()
                    .build());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Use BlockCanary to Detect Lag

// Initialize BlockCanary
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        BlockCanary.install(this, new AppBlockCanaryContext()).start();
    }
}

// Custom configuration
public class AppBlockCanaryContext extends BlockCanaryContext {
    @Override
    public int provideBlockThreshold() {
        return 500;  // Over 500ms considered lag
    }

    @Override
    public boolean displayNotification() {
        return BuildConfig.DEBUG;  // Show notification only in debug environment
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Use TraceView for Performance Analysis

// Add trace before and after suspicious code
Debug.startMethodTracing("my_trace");
// Suspicious code
suspiciousMethod();
Debug.stopMethodTracing();

// Export trace file
adb pull /sdcard/Android/data/<package>/files/my_trace.trace .

// Analyze using Android Studio's Profiler
Enter fullscreen mode Exit fullscreen mode

8.2 Online Monitoring System

1. ANR Capture and Reporting

public class ANRWatchDog extends Thread {
    private final int mTimeout;
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    public ANRWatchDog(int timeout) {
        super("ANR-WatchDog");
        this.mTimeout = timeout;
    }

    @Override
    public void run() {
        while (!isInterrupted()) {
            // 1. Record current time
            final long startTime = System.currentTimeMillis();
            final AtomicLong executionTime = new AtomicLong();

            // 2. Send task to main thread
            mHandler.post(() -> {
                executionTime.set(System.currentTimeMillis());
            });

            // 3. Wait for a period
            try {
                Thread.sleep(mTimeout);
            } catch (InterruptedException e) {
                return;
            }

            // 4. Check if task executed
            long execTime = executionTime.get();
            if (execTime == 0 || System.currentTimeMillis() - execTime > mTimeout) {
                // ANR detected!
                onAnrDetected();
            }
        }
    }

    private void onAnrDetected() {
        // 1. Collect stack trace information
        Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();

        // 2. Report to server
        ANRReporter.report(stackTraces);
    }
}

// Start monitoring
new ANRWatchDog(5000).start();
Enter fullscreen mode Exit fullscreen mode

2. Use Third-Party Platforms like Bugly

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // Initialize Bugly
        CrashReport.initCrashReport(getApplicationContext(), "YOUR_APP_ID", BuildConfig.DEBUG);

        // Configure ANR monitoring
        CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(getApplicationContext());
        strategy.setAppReportDelay(10000);  // Delayed reporting
        CrashReport.initCrashReport(getApplicationContext(), "YOUR_APP_ID", BuildConfig.DEBUG, strategy);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Monitor Key Metrics

public class PerformanceMonitor {
    // Monitor main thread message processing time
    public void monitorMainLooper() {
        Looper.getMainLooper().setMessageLogging(new Printer() {
            private static final String START = ">>>>> Dispatching";
            private static final String END = "<<<<< Finished";

            @Override
            public void println(String msg) {
                if (msg.startsWith(START)) {
                    // Message processing started
                    mStartTime = System.currentTimeMillis();
                } else if (msg.startsWith(END)) {
                    // Message processing finished
                    long duration = System.currentTimeMillis() - mStartTime;
                    if (duration > 100) {  // Over 100ms
                        // Log slow message
                        logSlowMessage(msg, duration);
                    }
                }
            }
        });
    }

    // Monitor method execution time
    @Around("execution(* com.example.app..*(..))")
    public Object measureMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        if (duration > 50) {  // Over 50ms
            String method = joinPoint.getSignature().toShortString();
            Log.w("Performance", method + " took " + duration + "ms");
        }

        return result;
    }
}
Enter fullscreen mode Exit fullscreen mode

8.3 Best Practices for ANR Optimization

1. Golden Rules for Main Thread

Main thread only does three things:
1. Update UI
2. Dispatch events
3. Launch async tasks

Absolutely must not do:
1. Disk I/O (read/write files, database)
2. Network I/O (HTTP requests, Socket communication)
3. Complex computation (big data processing, encryption/decryption)
4. Synchronous Binder calls (ContentProvider, System Service)
5. Wait for locks (synchronized, Lock)
Enter fullscreen mode Exit fullscreen mode

2. Use Async Mechanisms

Scenario Recommended Solution Example
Simple async tasks HandlerThread + Handler Background data processing
Lifecycle-aware ViewModel + LiveData UI data loading
Complex coroutines Kotlin Coroutines Network request + database
Scheduled tasks WorkManager Periodic sync
File I/O Executors.newSingleThreadExecutor() Log writing
Database Room + LiveData Local data access

3. Optimize Binder Calls

// ❌ Synchronous ContentProvider call on main thread
Cursor cursor = getContentResolver().query(uri, null, null, null, null);

// ✅ Use ContentProviderOperation for batch operations
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ops.add(ContentProviderOperation.newInsert(uri).withValues(values1).build());
ops.add(ContentProviderOperation.newInsert(uri).withValues(values2).build());
getContentResolver().applyBatch(AUTHORITY, ops);

// ✅ Use AsyncQueryHandler for async queries
new AsyncQueryHandler(getContentResolver()) {
    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        processData(cursor);
    }
}.startQuery(0, null, uri, null, null, null, null);
Enter fullscreen mode Exit fullscreen mode

4. Reduce Lock Contention

// ❌ Coarse-grained lock
public class DataCache {
    private final Map<String, Data> mCache = new HashMap<>();

    public synchronized Data get(String key) {
        Data data = mCache.get(key);
        if (data == null) {
            data = loadFromDisk(key);  // Time-consuming operation while holding lock!
            mCache.put(key, data);
        }
        return data;
    }
}

// ✅ Fine-grained lock + Double-Check
public class DataCache {
    private final ConcurrentHashMap<String, Data> mCache = new ConcurrentHashMap<>();
    private final Object mLock = new Object();

    public Data get(String key) {
        Data data = mCache.get(key);
        if (data == null) {
            synchronized (mLock) {
                data = mCache.get(key);
                if (data == null) {
                    data = loadFromDisk(key);  // Only hold lock when necessary
                    mCache.put(key, data);
                }
            }
        }
        return data;
    }
}

// ✅ Use Lock + tryLock to avoid dead wait
public class DataCache {
    private final ReentrantLock mLock = new ReentrantLock();

    public Data get(String key) {
        if (mLock.tryLock(100, TimeUnit.MILLISECONDS)) {
            try {
                return loadData(key);
            } finally {
                mLock.unlock();
            }
        } else {
            // Failed to acquire lock, return default value or async load
            return getDefaultValue();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

IX. Summary and Outlook

Let's review the full picture of the ANR mechanism:

9.1 Core Points Review

The 4 Types of ANR:

  • Input ANR: 5 seconds no response, detected by InputDispatcher
  • Broadcast ANR: 10s/60s, detected by ActivityManagerService
  • Service ANR: 20s/200s, detected by ActivityManagerService
  • ContentProvider ANR: 10 seconds, detected by ActivityManagerService

ANR Detection Mechanism:

  • Input: Record timestamp when event enters queue, periodically check waitQueue
  • Broadcast/Service: Send delayed message, timeout triggers Handler callback

ANR Processing Flow:

Detect timeout → Call appNotResponding() → Collect CPU/memory info
→ Dump thread stacks → Generate traces.txt → Write to DropBox → Pop Dialog
Enter fullscreen mode Exit fullscreen mode

Key Information in traces.txt:

  • Thread state: Blocked/Waiting/Native/Runnable
  • Stack information: Method call chain
  • Lock information: waiting to lock / locked

9.2 ANR Optimization Pyramid

02-04-anr-prevention-priority

9.3 Practical Recommendations

  1. Development Phase:

    • Enable StrictMode
    • Use BlockCanary
    • Code Review focus on main thread operations
  2. Testing Phase:

    • Stress testing
    • Monkey testing
    • Performance testing (TraceView, Systrace)
  3. Production Phase:

    • Integrate monitoring platforms like Bugly
    • Regularly analyze ANR Top charts
    • Establish ANR rapid response mechanism

9.4 Preview of Upcoming Articles

ANR mechanism is just one aspect of stability. In subsequent articles, we will continue to dive deeper:

Article Topic Core Content
Article 3 ANR Troubleshooting Practice Deep interpretation of traces.txt, Systrace analysis, common ANR cases
Article 4 Exception Logging Mechanism Crash handling, Tombstone analysis, signal mechanism
Article 5 Deep Analysis of Native Crash debuggerd, symbolization, coredump
Article 6 Java Exception and JE Analysis Practice Analysis practice of Java exceptions
Article 7 Watchdog Mechanism System watchdog, semi-deadlock detection, System Server protection

References

  1. Android Official Documentation - ANR
  2. AOSP Source Code - InputDispatcher.cpp
  3. AOSP Source Code - ActivityManagerService.java
  4. AOSP Source Code - BroadcastQueue.java
  5. Android Performance Patterns

Previous Article: Article 1 - Android Stability Basics: System Architecture and Key Mechanisms

Next Article: Article 3 - ANR Troubleshooting Practice: Log Analysis and Tool Practice

Back to Series Index: Android Stability & Performance Deep Understanding Series Introduction


Author Bio: Years of Android system development experience, specializing in system stability and performance optimization. Welcome to follow this series and explore the wonderful world of Android systems together!


🎉 Thanks for Following, Let's Explore the Wonderful World of Android Systems Together!

Find Me: Homepage

Top comments (0)