开发自定义崩溃上报器:如何捕获Android致命崩溃并上报至自定义后端
Great question! When building your own crash reporting pipeline for Android instead of relying on third-party tools like Firebase, the primary way to catch fatal crashes is by leveraging the Thread.UncaughtExceptionHandler interface. Let's walk through how to implement this effectively, along with key best practices:
First, create a class that implements Thread.UncaughtExceptionHandler—this will be your crash handler that captures, processes, and reports the crash data. It's important to keep a reference to the system's default handler so you can let Android handle the crash cleanup after you've sent your report.
Here's a Kotlin example (Java implementation follows the same logic):
class CustomCrashHandler(private val defaultSystemHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler { override fun uncaughtException(thread: Thread, throwable: Throwable) { // Step 1: Gather all relevant crash details val crashReport = StringBuilder().apply { append("Crash Timestamp: ${System.currentTimeMillis()}\n") append("Affected Thread: ${thread.name}\n") append("Full Stack Trace:\n${throwable.stackTraceToString()}\n") // Add metadata to make debugging easier append("App Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})\n") append("Device Model: ${Build.MODEL}\n") append("Android Version: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})") // You can add more: user ID, current screen, network state, etc. }.toString() // Step 2: Send the report to your backend // WARNING: Never make network calls on the main thread here—use a background thread Thread { try { // Replace this with your actual API call logic val client = OkHttpClient() val request = Request.Builder() .url("https://your-custom-backend.com/api/crash-reports") .post(RequestBody.create(MediaType.parse("text/plain"), crashReport)) .build() client.newCall(request).execute() } catch (uploadError: Exception) { // If upload fails, store the report locally to retry later // Example: Save to SharedPreferences or a local file saveCrashReportLocally(crashReport) } finally { // Let the system handle the crash exit process defaultSystemHandler.uncaughtException(thread, throwable) } }.start() } private fun saveCrashReportLocally(report: String) { // Implement your local storage logic here } }
To make your handler work across the entire app, register it in your custom Application class (this runs when your app starts up):
class MyApplication : Application() { override fun onCreate() { super.onCreate() // Grab the system's default crash handler val defaultHandler = Thread.getDefaultUncaughtExceptionHandler() // Set our custom handler as the default Thread.setDefaultUncaughtExceptionHandler(CustomCrashHandler(defaultHandler)) } }
Don't forget to declare this Application class in your AndroidManifest.xml:
<application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApp"> <!-- Your activities/services go here --> </application>
- Background Thread for Uploads: The main thread is in a crashed state when your handler runs—never perform network operations or heavy work here. Always spin up a background thread to send the report.
- Local Fallback for Failed Uploads: If the device is offline or your backend is down, save the crash report locally (SharedPreferences, SQLite, or a file) and retry the upload on the next app launch.
- Obfuscation Handling: If you use ProGuard/R8 for code obfuscation, add these rules to keep stack traces readable:
Without these, your stack traces will show obfuscated class/method names, making debugging nearly impossible.-keepattributes SourceFile,LineNumberTable -renamesourcefileattribute SourceFile - ANR Detection: Note that
UncaughtExceptionHandleronly catches uncaught exceptions. To detect ANRs (Application Not Responding), you'll need to monitor the/data/anr/traces.txtfile using aFileObserver—though this has permission restrictions on newer Android versions (API 29+), so you'll need to handle that carefully. - Multi-Process Apps: If your app uses multiple processes, you'll need to register the handler in each process's initialization code, since the main process's handler won't apply to other processes.
If your app includes native (C/C++) code, the above handler won't catch native crashes. For those, you'll need to use signal handlers (like signal(SIGSEGV, ...)) to capture the crash, generate a stack trace, and then report it. This requires more low-level work, but it's doable if you need full coverage.
内容的提问来源于stack exchange,提问作者zundi

