Android App Crash Patterns and Fixes

Type: Software Reference Confidence: 0.92 Sources: 7 Verified: 2026-02-23 Freshness: 2026-02-23

TL;DR

Constraints

Quick Reference

#CauseLikelihoodSignatureFix
1NullPointerException~35%java.lang.NullPointerException: Attempt to invoke virtual method '...' on a null object referenceUse Kotlin null safety (?., ?:); annotate Java with @NonNull/@Nullable
2ANR — main thread I/O~15%ANR in com.example.app + main thread in WAITING/BLOCKEDMove all disk/network I/O to coroutines (Dispatchers.IO) or WorkManager
3OutOfMemoryError~10%java.lang.OutOfMemoryError: Failed to allocate a N-byte allocationUse Glide/Coil for images; set inSampleSize; avoid static bitmap references
4IllegalStateException (Fragment)~8%IllegalStateException: Can not perform this action after onSaveInstanceStateUse commitAllowingStateLoss() or check lifecycle.currentState.isAtLeast(STARTED)
5SecurityException~6%SecurityException: Permission Denial: ... requires android.permission.XCheck ContextCompat.checkSelfPermission() before calling protected APIs
6SIGSEGV (native)~5%signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0Use AddressSanitizer; upload native debug symbols; check for use-after-free
7TransactionTooLargeException~4%TransactionTooLargeException: data parcel size N bytesKeep onSaveInstanceState bundles < 500KB; use ViewModel for large state
8IndexOutOfBoundsException~4%IndexOutOfBoundsException: Index: N, Size: MValidate list sizes; use getOrNull(); synchronize concurrent list modifications
9DeadObjectException~3%android.os.DeadObjectException at IPC call siteWrap Binder calls in try/catch; reconnect on failure
10ClassCastException~2%ClassCastException: X cannot be cast to YUse is checks; prefer generics; use typed Bundle getters
11SIGABRT (native)~2%signal 6 (SIGABRT), code -6 (SI_TKILL) + FORTIFY: ...Fix buffer overflows; review assert() conditions
12ConcurrentModificationException~2%ConcurrentModificationException during iterationUse CopyOnWriteArrayList or toMutableList() before modification

Decision Tree

START
├── Is it an ANR (dialog says "App isn't responding")?
│   ├── YES → Check main thread state in traces.txt
│   │   ├── Main thread BLOCKED on I/O → Move to Dispatchers.IO (Cause #2)
│   │   ├── Main thread WAITING on lock → Fix lock contention or deadlock
│   │   └── Main thread in long computation → Move to Dispatchers.Default
│   └── NO ↓
├── Is it a Java/Kotlin exception?
│   ├── YES → Read the exception class name
│   │   ├── NullPointerException → Cause #1: check null safety
│   │   ├── OutOfMemoryError → Cause #3: profile heap with Android Profiler
│   │   ├── IllegalStateException → Cause #4: check fragment/activity lifecycle
│   │   ├── SecurityException → Cause #5: check runtime permissions
│   │   ├── TransactionTooLargeException → Cause #7: reduce bundle size
│   │   ├── IndexOutOfBoundsException → Cause #8: validate bounds
│   │   ├── DeadObjectException → Cause #9: handle IPC failures
│   │   ├── ClassCastException → Cause #10: use type checks
│   │   └── ConcurrentModificationException → Cause #12: synchronize access
│   └── NO ↓
├── Is it a native crash (signal in logcat)?
│   ├── YES → Read the signal number
│   │   ├── SIGSEGV (signal 11) → Cause #6: null ptr / use-after-free in C/C++
│   │   ├── SIGABRT (signal 6) → Cause #11: assertion failure / buffer overflow
│   │   ├── SIGBUS → Misaligned memory access in native code
│   │   └── SIGFPE → Division by zero in native code
│   └── NO ↓
└── DEFAULT → Enable StrictMode + Crashlytics; reproduce with adb logcat filtering

Step-by-Step Guide

1. Capture the crash trace

Connect the device via USB and start logcat with crash filtering. [src1]

# Filter for fatal crashes and ANRs
adb logcat *:E | grep -E "FATAL EXCEPTION|ANR in|signal [0-9]+"

# Save full logcat to file for analysis
adb logcat -d > crash_log.txt

Verify: adb logcat -d | grep "FATAL EXCEPTION" → shows crash with full stack trace, thread name, PID.

2. Identify the crash category

Parse the first line of the exception to determine the crash type. Match against the Quick Reference table. [src7]

# Java/Kotlin crash format:
FATAL EXCEPTION: main
Process: com.example.app, PID: 12345
java.lang.NullPointerException: Attempt to invoke virtual method
    'int java.lang.String.length()' on a null object reference
    at com.example.app.MainActivity.processData(MainActivity.kt:42)

# Native crash format:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 12345, tid: 12345, name: main  >>> com.example.app <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0

Verify: You can identify the exception class, exact file/line number, and thread name from the trace.

3. Check if it's lifecycle-related

For crashes involving fragments, activities, or configuration changes, verify the component lifecycle state. [src6]

// Add lifecycle logging to narrow down timing
class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch {
            val data = withContext(Dispatchers.IO) { repository.fetchData() }
            if (viewLifecycleOwner.lifecycle.currentState
                    .isAtLeast(Lifecycle.State.STARTED)) {
                binding.textView.text = data
            }
        }
    }
}

Verify: adb logcat | grep "Lifecycle" — crash should not occur after DESTROYED state.

4. Profile memory for OOM crashes

Use Android Studio Profiler to capture heap dumps and track allocations. [src5]

# Dump heap from command line
adb shell am dumpheap com.example.app /data/local/tmp/heap.hprof
adb pull /data/local/tmp/heap.hprof

# Check process memory limits
adb shell getprop dalvik.vm.heapsize

Verify: Open .hprof in Android Studio Memory Profiler — look for retained Bitmap and byte[] allocations.

5. Retrieve ANR traces

Pull the ANR trace files to analyze which thread is blocked. [src2]

# List ANR trace files
adb shell ls /data/anr/

# Pull the most recent trace
adb pull /data/anr/traces.txt

# On Android 11+, use ApplicationExitInfo
adb shell dumpsys activity exit-info com.example.app

Verify: In traces.txt, find the main thread — its state (BLOCKED, WAITING) reveals the ANR cause.

6. Symbolicate native crashes

Use ndk-stack to convert raw addresses to source locations. [src3]

# Symbolicate from logcat output
adb logcat | ndk-stack -sym path/to/obj/local/arm64-v8a/

# From tombstone file
adb pull /data/tombstones/tombstone_00
ndk-stack -sym path/to/obj/local/arm64-v8a/ -dump tombstone_00

Verify: Output shows source file paths and line numbers instead of raw hex addresses.

Code Examples

Kotlin: Safe null handling patterns

// Input:  Nullable data from Intent extras, Bundle, or Java interop
// Output: Crash-free access with sensible defaults

// Safe call + Elvis for default value
val userName: String = intent.getStringExtra("user_name") ?: "Guest"

// let-block for conditional execution
intent.getStringExtra("deep_link")?.let { url ->
    navigator.handleDeepLink(url)
}

// require for fail-fast with clear messages
fun processOrder(orderId: String?) {
    requireNotNull(orderId) { "orderId must not be null" }
    repository.loadOrder(orderId) // smart-cast to non-null
}

Kotlin: Coroutine-safe lifecycle-aware operations

// Input:  Async operations that outlive the Activity/Fragment
// Output: Automatic cancellation, no IllegalStateException

class SearchFragment : Fragment(R.layout.fragment_search) {
    private val viewModel: SearchViewModel by viewModels()

    override fun onViewCreated(view: View, s: Bundle?) {
        super.onViewCreated(view, s)
        viewLifecycleOwner.lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { updateUI(it) }
            }
        }
    }
}

Java: Defensive permission checking

// Input:  Runtime permission request before camera access
// Output: Graceful handling without SecurityException crash

private void openCamera() {
    if (ContextCompat.checkSelfPermission(this, CAMERA)
            == PackageManager.PERMISSION_GRANTED) {
        launchCamera();
    } else if (shouldShowRequestPermissionRationale(CAMERA)) {
        showPermissionRationale();
    } else {
        requestPermissions(new String[]{CAMERA}, REQ_CAMERA);
    }
}

Java: Safe bitmap loading to prevent OOM

// Input:  Large image file path
// Output: Downsampled bitmap that fits in memory

public static Bitmap decodeSampledBitmap(String path,
        int reqW, int reqH) {
    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, opts);
    opts.inSampleSize = calcSampleSize(opts, reqW, reqH);
    opts.inJustDecodeBounds = false;
    opts.inPreferredConfig = Bitmap.Config.RGB_565;
    return BitmapFactory.decodeFile(path, opts);
}

Anti-Patterns

Wrong: Catching generic Exception to suppress crashes

// BAD — hides real bugs, causes silent data corruption
try {
    val result = processPayment(order)
    updateUI(result)
} catch (e: Exception) {
    // Swallowed — no logging, no user feedback
}

Correct: Catch specific exceptions with proper handling

// GOOD — catches expected failures, lets real bugs surface
try {
    val result = processPayment(order)
    updateUI(result)
} catch (e: IOException) {
    Log.w(TAG, "Network error", e)
    showRetryDialog()
} catch (e: PaymentDeclinedException) {
    showDeclinedMessage(e.reason)
}

Wrong: Network calls on the main thread

// BAD — causes ANR after 5 seconds
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val response = URL("https://api.example.com/data").readText()
    binding.textView.text = response
}

Correct: Use coroutines with appropriate dispatcher

// GOOD — non-blocking, lifecycle-aware
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    lifecycleScope.launch {
        val response = withContext(Dispatchers.IO) {
            URL("https://api.example.com/data").readText()
        }
        binding.textView.text = response
    }
}

Wrong: Storing Activity references in static fields

// BAD — leaks the entire Activity, leads to OOM
companion object {
    var currentActivity: Activity? = null
    val cachedBitmap: Bitmap? = null
}

Correct: Use ViewModel or WeakReference

// GOOD — ViewModel survives config changes without leaking
class MainViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data
    fun loadData() {
        viewModelScope.launch { _data.value = repository.fetchData() }
    }
}

Wrong: Fragment transaction after onSaveInstanceState

// BAD — crashes with IllegalStateException
fun onDataLoaded(data: Data) {
    supportFragmentManager.beginTransaction()
        .replace(R.id.container, DetailFragment.newInstance(data))
        .commit() // CRASH if after onSaveInstanceState
}

Correct: Check lifecycle state before transaction

// GOOD — guards against state loss
fun onDataLoaded(data: Data) {
    if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
        supportFragmentManager.beginTransaction()
            .replace(R.id.container, DetailFragment.newInstance(data))
            .commit()
    } else {
        pendingFragment = DetailFragment.newInstance(data)
    }
}

Common Pitfalls

Diagnostic Commands

# Capture crash logcat in real time
adb logcat *:E

# Filter for fatal exceptions only
adb logcat | grep -E "FATAL EXCEPTION|Process.*PID"

# Check ANR traces
adb shell ls -la /data/anr/
adb pull /data/anr/traces.txt ./anr_traces.txt

# Dump current process memory stats
adb shell dumpsys meminfo com.example.app

# Check memory limits on device
adb shell getprop dalvik.vm.heapsize
adb shell getprop dalvik.vm.heapgrowthlimit

# Monitor GC activity in real time
adb logcat -s "art" | grep -i "gc"

# Get application exit reasons (Android 11+)
adb shell dumpsys activity exit-info com.example.app

# Symbolicate native crash from tombstone
ndk-stack -sym path/to/obj/local/arm64-v8a/ -dump tombstone_00

# List tombstone files from native crashes
adb shell ls -la /data/tombstones/

# Capture a bug report with full system state
adb bugreport > bugreport.zip

Version History & Compatibility

Android VersionStatusCrash-Related ChangesMigration Notes
Android 15 (API 35)PreviewStricter background process limits; tombstone format improvementsTest background services under new restrictions
Android 14 (API 34)CurrentForeground service type required; stricter implicit intentsAdd foregroundServiceType to manifest
Android 13 (API 33)SupportedRuntime notification permission; per-app languageAdd POST_NOTIFICATIONS permission
Android 12 (API 31)SupportedTombstone collection via Crashlytics NDK; strict exported flagSet android:exported on all intent-filter components
Android 11 (API 30)SupportedApplicationExitInfo API; AsyncTask deprecatedUse getHistoricalProcessExitReasons()
Android 10 (API 29)MaintenanceScoped storage; background activity launch restrictionsUpdate file access; use PendingIntent for background launches

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Debugging production crash reports from Crashlytics/Play ConsoleCrash is a known SDK bug with a pending fixWait for SDK update; add workaround
Setting up crash prevention patterns in a new projectIssue is a build error or compilation failureStandard build error debugging
Profiling ANR rates during performance optimizationIssue is slow rendering (jank) without ANRAndroid GPU Inspector or Frame Profiler
Investigating OOM on low-memory devicesMemory is high but no actual crash occursMemory Profiler for optimization
Training team on crash-free coding practicesCrash only occurs in unit testsCheck test framework and mocking setup

Important Caveats

Related Units