Capturing and Logging Unhandled Exceptions in Android

To handle uncaught exceptions globally in an Android app, you can set up a custom UncaughtExceptionHandler that collects crash data and persists it before the app terminates.

Registering the Handler

Create a custom Application subclass and override onCreate(). Assign your custom handler as the default uncaught expection handler for the main thread.

import android.app.Application;

public class CrashShield extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Thread.setDefaultUncaughtExceptionHandler(
                CrashCollector.getInstance(getApplicationContext()));
    }
}

Declare this class in the manifest under the <application> element:

<application
    android:name=".CrashShield"
    ... >
    ...
</application>

Building the Crash Collector

Implement Thread.UncaughtExceptionHandler as a thread-safe singleton that stores the origianl system handler and collects relevant information when a crash occurs.

import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.Process;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

public class CrashCollector implements Thread.UncaughtExceptionHandler {
    private static CrashCollector instance;
    private final Context appContext;
    private final Thread.UncaughtExceptionHandler systemHandler;

    private CrashCollector(Context ctx) {
        appContext = ctx.getApplicationContext();
        systemHandler = Thread.getDefaultUncaughtExceptionHandler();
    }

    public static synchronized CrashCollector getInstance(Context ctx) {
        if (instance == null) {
            instance = new CrashCollector(ctx);
        }
        return instance;
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        StringWriter stackTraceWriter = new StringWriter();
        ex.printStackTrace(new PrintWriter(stackTraceWriter));

        StringBuilder logEntry = new StringBuilder();
        try {
            PackageInfo packageInfo = appContext.getPackageManager()
                    .getPackageInfo(appContext.getPackageName(), 0);
            logEntry.append("App version: ").append(packageInfo.versionName).append("\n");
        } catch (Exception ignored) {}

        logEntry.append("Thread: ").append(thread.getName()).append("\n");
        logEntry.append("Stack trace:\n").append(stackTraceWriter.toString()).append("\n");

        // Append device metadata
        logEntry.append("Device info:\n");
        logEntry.append("Manufacturer: ").append(Build.MANUFACTURER).append("\n");
        logEntry.append("Model: ").append(Build.MODEL).append("\n");
        logEntry.append("OS version: ").append(Build.VERSION.RELEASE).append("\n");
        logEntry.append("SDK level: ").append(Build.VERSION.SDK_INT).append("\n");
        logEntry.append("Timestamp: ").append(System.currentTimeMillis()).append("\n");

        writeToFile(logEntry.toString());

        android.os.Process.killProcess(Process.myPid());
        if (systemHandler != null) {
            systemHandler.uncaughtException(thread, ex);
        }
    }

    private void writeToFile(String content) {
        File logDir = appContext.getExternalFilesDir(null);
        if (logDir == null) return;
        if (!logDir.exists()) logDir.mkdirs();

        File crashLog = new File(logDir, "crash_report.log");
        try (FileOutputStream fos = new FileOutputStream(crashLog, true)) {
            fos.write(content.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

When an exception reaches the handler, it writes the app version, thread name, full stack trace, device properties, and a timestamp to a log file in the app's private external storage directory. Afterwards, the process is killed and the original system handler is invoked.

Tags: Android Exception Handling UncaughtExceptionHandler crash logging

Posted on Mon, 18 May 2026 19:59:54 +0000 by rfighter