Mobile Development 19 min read

How to Capture and Analyze Android Crashes: From Java Exceptions to Native Minidumps

This technical guide explains the underlying mechanisms of Android crash collection for both Java/Kotlin and native code, outlines a four‑step capture process, compares popular solutions, and provides concrete implementation details—including signal handling, minidump generation, and stack deobfuscation—to help developers reliably record and diagnose app failures.

Alibaba Cloud Native
Alibaba Cloud Native
Alibaba Cloud Native
How to Capture and Analyze Android Crashes: From Java Exceptions to Native Minidumps

Background

Stability is crucial for mobile apps; crashes lead to user dissatisfaction, negative reviews, and uninstalls. Android applications often present only a generic “stopped working” prompt, making diagnosis difficult, especially for native crashes and code that has been obfuscated.

Crash Types and Capture Principles

Two main crash categories exist on Android:

Java/Kotlin exceptions run on the Android Runtime (ART). When an uncaught exception such as NullPointerException propagates to the top of a thread, ART invokes the Thread.UncaughtExceptionHandler callback.

Native C/C++ crashes are not managed by ART. They occur when the CPU executes an illegal instruction or accesses invalid memory, causing the Linux kernel to send a signal (e.g., SIGSEGV, SIGILL) to the process.

Signal Details

SIGSEGV (Segmentation Fault) – invalid memory access.

SIGILL (Illegal Instruction) – CPU cannot decode the instruction.

SIGABRT (Abort) – process voluntarily aborts, often via abort() or failed assertions.

SIGFPE (Floating‑Point Exception) – arithmetic errors such as division by zero.

Four‑Step Capture Process

Register a handler : Use sigaction for signals and Thread.setDefaultUncaughtExceptionHandler for Java.

Operate in an async‑signal‑safe environment : Avoid functions like malloc, free, printf, strcpy. Use only safe calls such as write, open, read.

Stack unwinding : When possible, employ libraries like libunwind to reconstruct the call stack.

Generate a minidump : Record registers, raw stack memory, thread list, and loaded modules into a structured minidump file for later offline analysis.

Key Technical Challenges

Capture timing and data persistence : The process is in a fragile state; information must be written synchronously to local storage before termination.

Native crashes as a “black box” : Stack corruption often makes traditional unwinding unreliable, so a full snapshot (minidump) is required.

Obfuscation and symbolization : ProGuard/R8 replace class and method names with meaningless tokens, and native binaries contain only raw addresses. Proper mapping files ( mapping.txt for Java, DWARF info for native) are needed to translate back to readable symbols.

Industry Solutions

Google Breakpad/Crashpad – the de‑facto standard for native crash capture, providing end‑to‑end minidump generation and symbolication tools.

Firebase Crashlytics & Sentry – SaaS platforms that wrap the low‑level capture logic and offer cloud dashboards for aggregation and analysis.

xCrash – an open‑source library supporting both Java and native crashes with advanced stack recovery.

Implementation Overview

Java/Kotlin Crash Handling

Register a global handler that records exception type, message, stack trace, thread name, and timestamp, then persists the data synchronously using SharedPreferences.edit().commit(). The original handler is invoked afterwards to preserve default system behavior.

@Override
public void uncaughtException(Thread thread, Throwable throwable) {
    try {
        CrashData crashData = collectCrashData(thread, throwable);
        saveCrashData(crashData);
    } finally {
        if (originalHandler != null) {
            originalHandler.uncaughtException(thread, throwable);
        }
    }
}

private void saveCrashData(CrashData data) {
    // Use synchronous commit to guarantee persistence
    prefs.edit().putString("last_crash", data.toJson()).commit();
}

Native Crash Handling

At application start, load a native library and call NativeBridge.initialize(dumpPath). The library registers signal handlers for SIGSEGV, SIGILL, SIGABRT, and SIGFPE using sigaction with the SA_SIGINFO flag to obtain a siginfo_t structure. Inside the handler only async‑signal‑safe operations are performed: write registers, stack memory, and module list to a .dmp file.

public void start() {
    // Initialize native signal handlers
    NativeBridge.initialize(crashDir.getAbsolutePath());
    // Process any leftover dumps on next launch
    new Thread(this::processExistingDumps).start();
}

private void processExistingDumps() {
    File[] dumpFiles = crashDir.listFiles();
    for (File dumpFile : dumpFiles) {
        reportToServer(dumpFile);
        dumpFile.delete();
    }
}

static class NativeBridge {
    static { System.loadLibrary("crash-handler"); }
    public static native void initialize(String dumpPath);
}

Minidump Processing

On the next app launch the framework scans the dump directory, parses each minidump (using Breakpad tools such as dump_syms and minidump_stackwalk on the server), extracts thread contexts, registers, and module information, uploads the report, and deletes the file.

Java Stack Deobfuscation

When code is minified with ProGuard/R8, a mapping.txt file maps obfuscated names to original class and method names. A deobfuscation tool reads each stack line, extracts the obfuscated class, method, and line number, looks up the original names in mapping.txt, and restores the correct source location.

Native Stack Symbolication

For native addresses, the unstripped .so contains DWARF debug information. The NDK utility

addr2line -C -f -e /path/to/unstripped/libtest-native.so 0x3538

translates the address to a function name and source file line, demangling C++ symbols as needed.

# Using NDK addr2line
# -C: demangle C++ symbols
# -f: show function name
# -e: specify the unstripped library
addr2line -C -f -e /path/to/unstripped/libtest-native.so 0x3538

Conclusion

The article dissects Android crash capture, addresses three core difficulties—capture timing, the black‑box nature of native crashes, and stack obfuscation—and proposes a unified framework that leverages Java’s UncaughtExceptionHandler and Breakpad‑based native handling to reliably collect, persist, and later analyse crash data.

JavanativeAndroidObfuscationCrash HandlingminidumpSignal
Alibaba Cloud Native
Written by

Alibaba Cloud Native

We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.