Краш додатка: що це, причини вильотів і методи відлову

Автор: IT Sectr Опубліковано: 2026-07-27 Час читання: 7 хв

Краш додатка — аварійне завершення, при якому програма перестає відповідати і закривається. У мобільній розробці краші — основне джерело негативних відгуків і зниження рейтингу. За даними Firebase (2024), користувачі видаляють додаток після одного-двох крашів у 53% випадків. Кожен виліт знижує retention на 3–5%. Системи моніторингу на кшталт Crashlytics і Sentry допомагають швидко знаходити й виправляти причини падінь до масового впливу на користувачів.

Головне

  • Краш — неочікуване завершення додатка через необроблену помилку часу виконання
  • Основні причини — NullPointerException, OutOfMemoryError, IndexOutOfBounds, ANR в Android
  • Crashlytics — стандарт моніторингу крашів з автоматичним збором stack trace і групуванням
  • Runtime exceptions — винятки, які компілятор не перевіряє, вони проявляються тільки в рантаймі
  • Стратегії запобігання — строга типізація, optional binding, error handling і тестування

Що таке краш додатка

Краш — неочікуване завершення програми, спричинене винятковою ситуацією, яку код не обробив. У мобільних ОС краш призводить до негайного закриття додатка й показу екрана “Додаток зупинено” або повернення на домашній екран.

Краші поділяються на два великі класи. Оброблені помилки — блоки try/catch перехоплюють виняток, додаток продовжує працювати, можливо з втратою функціональності. Необроблені краші — виняток спливає до рівня ОС, і система вбиває процес. Другий тип особливо небезпечний, оскільки користувач не може зберегти дані.

Система з двома мільйонами користувачів і 0.1% crash rate втрачає 2 000 користувачів на кожному релізі. За даними Google Play Console (2024), додатки з crash rate вище 1.5% виключаються з рекомендацій і втрачають до 30% органічного трафіку.

Основні причини вильотів у мобільних додатках

NullPointerException (NPE) — король крашів у Java/Kotlin. Спроба викликати метод на null-об'єкті. У Kotlin NPE трапляється рідше завдяки null safety, але все ще можливий при використанні оператора !! або взаємодії з Java-кодом. Google (2024) оцінює: NPE становлять 25% усіх крашів Android-додатків.

IndexOutOfBoundsException — звернення до елемента списку за неіснуючим індексом. Часта причина: дані прийшли з сервера в неочікуваному форматі, а UI намагається відобразити позицію, якої немає. Рішення — завжди перевіряй розмір колекції перед доступом за індексом.

ANR (Application Not Responding) — Android-специфічна проблема. UI-потік блокується більш ніж на 5 секунд. Основні причини: мережеві запити на головному потоці, важкі обчислення, синхронізація з базою даних. StrictMode в Android допомагає виявити блокування UI-потоку на етапі розробки.

OutOfMemoryError (OOM) — додаток перевищив ліміт пам'яті. На мобільних пристроях з 2–4 GB RAM OOM — часта проблема при роботі з великими зображеннями або нескінченними списками без пагінації. Рішення — Glide/Coil для завантаження зображень, LruCache для кешування, ViewHolder у RecyclerView.

Runtime exceptions і фатальні помилки

Runtime exceptions — помилки, які компілятор не перевіряє на етапі збірки. Вони проявляються тільки при виконанні коду на конкретному пристрої з конкретними даними. У Java це RuntimeException і його підкласи: NullPointerException, IllegalArgumentException, ArithmeticException.

Фатальні помилки (FATAL) — не runtime, а системні збої. Signal 11 (SIGSEGV) — порушення сегментації пам'яті в native-коді. Signal 6 (SIGABRT) — аварійне завершення, викликане самим додатком через abort(). Такі краші важко діагностувати, оскільки stack trace часто не показує зрозумілого контексту.

В iOS основні причини — NSInvalidArgumentException (неочікуваний nil у параметрі) і EXC_BAD_ACCESS (доступ до звільненої пам'яті). Swift зменшив кількість крашів порівняно з Objective-C, але помилки в ObjC runtime і C-бібліотеках все ще призводять до падінь.

Моніторинг і збір crash-логів

Firebase Crashlytics — стандарт для мобільних додатків. Автоматично збирає stack trace, додає логи, user ID і метадані пристрою. Групує краші за сигнатурою (клас помилки + рядок). Real-time alerts — сповіщення, коли crash rate перевищує заданий поріг (наприклад, >0.1% за годину).

Sentry — альтернатива з більш гнучкими можливостями. Дозволяє створювати custom contexts, додавати breadcrumbs (попередні події), налаштовувати in-app filtering для виключення неважливих помилок. Source maps для Kotlin і Swift дозволяють бачити вихідний код, а не обфусковані імена.

Best practices для логів: відправляй ключові метадані перед виконанням небезпечної операції — так у лозі буде видно, що робив користувач перед крашем. Додавай custom keys (номер версії API, останній екран, розмір вхідних даних). Це перетворює марний stack trace на actionable інформацію.

Приклад: налаштування Crashlytics в Android

kotlin
class PaymentViewModel : ViewModel() {
    fun processPayment(amount: Double) {
        crashlytics.setCustomKey("last_screen", "payment")
        crashlytics.setCustomKey("amount", amount)
        try {
            api.charge(amount)
        } catch (e: Exception) {
            crashlytics.recordException(e)
        }
    }
}

Стратегії запобігання крашам

Optional binding і null safety — у Kotlin використовуй `?` для nullable-типів, `let` і `?:` для безпечної обробки null. У Swift — optionals і guard let. Modern Kotlin (2024) додав Contract-анотації: `@ContractsDsl` дозволяє оголосити, що функція не повертає null, і компілятор це перевіряє.

Error handling в мережі — кожен мережевий запит повинен обробляти timeout, помилки парсингу та відмову сервера. Retrofit з Result-типом — sealed class, який гарантує, що помилка буде оброблена. No Exception-стиль: замість try/catch використовуй sealed Result для явної обробки успіху та помилки.

Feature flags — вимикай проблемний функціонал віддалено без релізу нової версії. Якщо серверна операція викликає краш на старих пристроях — флаг вимикає її для цієї групи. Firebase Remote Config дозволяє змінювати поведінку додатка без публікації в магазині.

Поступовий rollout — випускай нову версію на 5% аудиторії й моніторь crash rate. Якщо rate залишається нижче цільового (зазвичай <0.1%), розширюй до 25%, потім 50%, потім 100%. Google Play Console і App Store Connect підтримують staged rollouts для автоматичної зупинки при перевищенні порогу.

План дій при виявленні помилки

Крок 1: Класифікація — визнач severity: Critical (краш у >1% користувачів), High (0.1–1%), Medium (<0.1%). Для Critical крашів — immediate response. Для решти — стандартний процес багфіксу в поточному спринті. Google Play Console автоматично класифікує краші за кількістю зачеплених користувачів.

Крок 2: Аналіз stack trace — відкрий лог у Crashlytics, подивись точне місце падіння. Перевір custom keys: який екран, які дані, версія ОС. Співстав з останнім деплоєм — часто краш викликаний свіжою зміною в коді, яка зачепила неочікуваний сценарій використання.

Крок 3: Відтворення — спробуй відтворити краш на пристрої або емуляторі з аналогічними параметрами. Якщо не виходить — перевір crash log на патерни: конкретні моделі (Samsung A10), версії Android (API < 26), локалі. Рішення — додай захисну умову, яка покриває сценарій.

Крок 4: Фікс і моніторинг — випусти hotfix з пріоритетом. Після релізу переконайся, що crash rate по цьому типу падає до нуля. Напиши regression-тест, який покриває сценарій крашу. Без тесту той самий баг може повернутися в наступному рефакторингу.

Часті запитання

Який crash rate вважається нормальним?

Нормальний crash rate — менше 0.1% для production-релізів. Google Play рекомендує тримати crash rate нижче 1.5%, але топові додатки (YouTube, Instagram) тримають 0.01–0.05%. Для релізів нової функціональності допускається тимчасове зростання до 0.5% з наступним зниженням після хотфікса.

Чим краш відрізняється від ANR?

Краш — додаток аварійно завершується. ANR (Application Not Responding) — додаток зависає більш ніж на 5 секунд, але не закривається примусово. Користувач бачить діалог «Додаток не відповідає» і може зачекати або закрити. ANR-проблеми не менш серйозні, ніж краші, і теж впливають на рейтинг у магазині.

Чому краш може відтворюватися не на всіх пристроях?

Різні пристрої мають різні версії ОС, об'єм пам'яті, версії бібліотек і навіть процесори. Приклад: краш на Android 6 (API 23) через відсутність runtime permission може не відтворюватися на Android 12. Аналізуй crash log за фільтрами: OS version, device model, amount of RAM. Це вкаже на специфіку проблеми.

Як знайти причину крашу, якщо stack trace неінформативний?

Додай custom breadcrumbs у Crashlytics: записуй ключові події перед виконанням операції. Якщо краш виникає на 3-му кроці онбордингу — це вказує на проблему в конкретному екрані. Debug symbols (dSYM, ProGuard mapping) — обов'язково завантажуй у Crashlytics, щоб бачити реальні імена функцій, а не обфусковані.

Чи потрібно крашити додаток при нефатальних помилках?

У production — ніколи. Необроблений краш погіршує користувацький досвід. Використовуй try/catch з логуванням помилки. У debug-режимі допустимо крашити для швидкого зворотного зв'язку розробнику. Assertions — для перевірки інваріантів, які ніколи не повинні порушуватися, але тільки в debug-збірках.

Підсумки

  • Краш — аварійне завершення додатка, що веде до втрати користувачів і зниження рейтингу в магазинах
  • NullPointerException — найчастіша причина крашів у мобільних додатках (25% усіх падінь)
  • ANR і OOM — критичні Android-специфічні проблеми, що потребують окремого моніторингу та профілактики
  • Crashlytics і Sentry — основні інструменти збору stack trace з групуванням і real-time оповіщеннями
  • Error handling — optional binding, sealed Result-типи та захисні перевірки запобігають більшості крашів
  • Feature flags і staged rollout — знижують вплив багів на аудиторію, дозволяючи відкотити проблемний код
  • Після фіксу крашу обов'язковий regression-тест, що виключає рецидив проблеми

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також