shekhar23/claude-zero-to-hero-android-kmp/android-crash-reporting

Claude Code and agent skill by shekhar23/claude-zero-to-hero-android-kmp

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Crash reporting and ANR detection for Android - Firebase Crashlytics setup, non-fatal exception boundaries, custom keys and log lines, user identity rules, ANR detection, ViewModel crash boundaries, and debug vs release configuration. Use this skill whenever adding crash reporting, recording non-fatal errors, adding diagnostic context to crashes, setting up Crashlytics, or tuning crash triage workflows. Trigger on phrases like "crash reporting", "Crashlytics", "Firebase Crashlytics", "non-fatal", "ANR", "crash boundary", "recordException", "crash log", "custom key", "crash triage".

Install

npx vskill@latest install shekhar23/claude-zero-to-hero-android-kmp/android-crash-reporting

SKILL.md

Android Crash Reporting

Overview

Crash reporting surfaces real-world failures that never appear in tests. This skill covers the full setup from Gradle to triage, with strict rules on what context to attach and how to keep PII out of crash reports.

Stack: Firebase Crashlytics (primary), Strict Mode for ANR/violation detection.

Related skills: android-analytics-logging, android-error-handling.


Gradle Setup

kotlin
// build.gradle.kts (:app)
plugins {
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics)
}

Enable / Disable by Build Type

Never send debug crashes to Crashlytics — it pollutes production data.

kotlin
// build.gradle.kts (:app)
buildTypes {
debug {
manifestPlaceholders["crashlyticsEnabled"] = false
}
release {
manifestPlaceholders["crashlyticsEnabled"] = true
}
}
xml
<!-- AndroidManifest.xml -->
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlyticsEnabled}" />

Or programmatically in Application.onCreate():

kotlin
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)

CrashReporter Interface (Domain Layer)

Wrap Crashlytics behind an interface so presentation/domain never import Firebase directly.

kotlin
// domain layer
interface CrashReporter {
fun recordException(throwable: Throwable)
fun log(message: String)
fun setKey(key: String, value: String)
fun setUserId(anonymousId: String) // NEVER real name/email
}
// data layer
class CrashlyticsReporter : CrashReporter {
private val c get() = FirebaseCrashlytics.getInstance()
override fun recordException(t: Throwable) = c.recordException(t)
override fun log(message: String) = c.log(message)
override fun setKey(key: String, value: String) = c.setCustomKey(key, value)
override fun setUserId(anonymousId: String) = c.setUserId(anonymousId)
}

Koin binding:

kotlin
single<CrashReporter> { CrashlyticsReporter() }

Non-Fatal Boundaries in ViewModels

Record exceptions at recovery boundaries — not deep inside business logic.

kotlin
class NotesViewModel(
private val repository: NoteRepository,
private val crashReporter: CrashReporter,
) : ViewModel() {
fun loadNotes() {
viewModelScope.launch {
when (val result = repository.getNotes()) {
is Result.Success -> { /* update state */ }
is Result.Error -> {
crashReporter.log("loadNotes failed: ${result.error}")
crashReporter.recordException(RuntimeException("loadNotes: ${result.error}"))
// update error UI state — do not rethrow
}
}
}
}
}

Custom Keys for Crash Context

Set keys immediately before the risky operation. Keys are overwritten each call — the last value before the crash is what Crashlytics shows.

kotlin
fun syncNotes() {
viewModelScope.launch {
crashReporter.setKey("sync_trigger", "manual")
crashReporter.setKey("note_count", noteCount.toString())
crashReporter.log("Starting note sync")
when (val result = repository.sync()) {
is Result.Error -> crashReporter.recordException(RuntimeException("sync: ${result.error}"))
is Result.Success -> crashReporter.log("Sync complete")
}
}
}

Safe to attach: feature name, screen name, anonymized entity IDs (hashed), operation type, non-sensitive counts.

Never attach: email, name, phone, auth tokens, passwords, raw user content.


ANR Detection with StrictMode

Enable in debug builds only to catch main-thread violations before they reach users.

kotlin
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}
}
}

ViewModel Crash Boundary

Uncaught exceptions in viewModelScope crash the app. Use a CoroutineExceptionHandler to record them.

kotlin
class BaseViewModel(private val crashReporter: CrashReporter) : ViewModel() {
protected fun safeLaunch(block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(
CoroutineExceptionHandler { _, throwable ->
crashReporter.log("Unhandled ViewModel exception: ${throwable.message}")
crashReporter.recordException(throwable)
}
) { block() }
}

Release Checklist

  • [ ] Crashlytics disabled in debug, enabled in release
  • [ ] No PII in setUserId — hashed/anonymous IDs only
  • [ ] Mapping file upload configured (Crashlytics Gradle plugin handles this automatically)
  • [ ] StrictMode zero violations in debug before merging to main
  • [ ] Main thread never blocked > 200ms
  • [ ] Non-fatal alert thresholds configured in Firebase console

Anti-Patterns

Anti-patternWhy it's wrongInstead
recordException in every catch blockFloods dashboard, hides real issuesRecord only at top-level boundaries
setUserId(email)PII violationUse a hashed or server-assigned anonymous ID
Crashlytics enabled in debugPollutes production dashboardGuard with BuildConfig.DEBUG
Logging full request/response bodiesMay contain tokens or PIILog only operation name and error code
Ignoring non-fatal volumeHigh-rate non-fatals degrade UX as much as crashesSet up Firebase alerts on non-fatal rate

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
9
Repository forks
5
7-day trend score
12

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/shekhar23/claude-zero-to-hero-android-kmp/android-crash-reporting/badge)](https://verified-skill.com/skills/shekhar23/claude-zero-to-hero-android-kmp/android-crash-reporting)

Something wrong with this skill or its page? Tell us.