Crash — terminarea forțată a aplicației mobile din cauza unei excepții nemanipulate sau a unei avarii fatale de sistem. Conform datelor Firebase Crashlytics, aproximativ 2% dintre utilizatori se confruntă zilnic cu avarii, iar fiecare cădere reduce retenția cu 10–20%. Înțelegerea cauzelor și metodelor de prevenire a avariilor este o abilitate obligatorie pentru dezvoltatorul mobil.
Principalele puncte
Crash — este terminarea forțată a aplicației cauzată de o excepție nemanipulată sau un semnal fatal de sistem care nu a fost gestionat în codul aplicației. Când sistemul sau mașina virtuală (JVM, ART) detectează o stare fatală — NullPointerException, IndexOutOfBoundsException, OutOfMemoryError — oprește imediat procesul și îl descarcă din memorie. Utilizatorul vede închiderea bruscă a aplicației fără nicio notificare de eroare de sistem. Conform Google, aplicațiile cu o rată crash-free sub 99% pierd până la 20% din utilizatorii activi pe lună.
Pe Android mecanismul de gestionare a avariilor diferă de sistemele desktop. În locul unui dialog de depanare cu stack trace, Android pur și simplu ucide procesul și nu salvează informații detaliate. Colectarea informațiilor despre avarie este sarcina bibliotecilor terțe (Crashlytics, Sentry, Bugsnag), care interceptează excepțiile prin Thread.setDefaultUncaughtExceptionHandler înainte ca procesul să fie terminat.
iOS folosește un mecanism similar cu NSException și Mach exceptions pentru gestionarea erorilor fatale. La o excepție nemanipulată, sistemul termină aplicația, iar raportul este salvat sub formă de fișier .crash. Colectarea avariilor pe iOS necesită integrarea cu Crashlytics sau raportul încorporat prin Xcode Organizer.
Cinci categorii de avarii acoperă 90% din toate căderile în aplicațiile mobile. Înțelegerea fiecărui tip ajută la diagnosticarea și rezolvarea mai rapidă a problemelor în producție.
NullPointerException (NPE) — cel mai răspândit tip de avarie în toate aplicațiile Java/Kotlin. Apare la încercarea de a apela o metodă sau de a accesa un câmp al unui obiect care este null. Scenarii tipice: câmp neinițializat al Activity la rotirea ecranului, răspuns null de la server la deserializarea JSON, navigare neglijentă prin adaptorul RecyclerView.
Kotlin rezolvă problema NPE la nivel de limbaj prin tipuri null-safe: String? nu poate fi folosit fără o verificare explicită. Totuși, compatibilitatea cu Java și Reflection creează în continuare riscuri. Utilizați adnotările @NonNull și @Nullable și activați strictNullChecks în instrumentele de analiză statică.
fun safeLength(text: String?): Int {
return text?.length ?: 0 // gestionarea sigură a null
}
IndexOutOfBoundsException apare la accesarea unui index inexistent al unei liste sau al unui tablou. Scenariu frecvent: eliminarea unui element din RecyclerView fără sincronizare cu adaptorul, modificarea multi-thread a ArrayList fără blocare, calcularea incorectă a poziției în ViewPager. ConcurrentModificationException — o rudă apropiată la iterarea și modificarea simultană a colecțiilor.
Utilizați CopyOnWriteArrayList pentru acces multi-thread sau colecții Lock-free din java.util.concurrent. Pentru sincronizarea cu UI, aplicați DiffUtil, care calculează diferența dintre lista veche și cea nouă în siguranță și eficient.
ClassCastException apare la convertirea unui obiect la un tip incompatibil. În Android, cauze tipice: tip incorect de ViewHolder în RecyclerView (tipuri diferite de celule fără getItemViewType corect), convertirea incorectă a Fragment la navigare, obiecte Serializable cu versiuni diferite de clase.
Utilizați conversia sigură Kotlin prin operatorul as?, care returnează null la incompatibilitate de tipuri. În Java — verificare prin instanceof înainte de conversie. Pentru obiectele Parcelable, declarați obligatoriu CREATOR în fiecare clasă.
IllegalStateException semnalizează apelarea unei metode într-o stare nepotrivită a obiectului. Exemplu tipic în Android — getSupportFragmentManager() după onSaveInstanceState, când commit() al fragmentului nu este permis. Un alt caz frecvent — apelarea dismiss() pe un dialog deja închis.
Verificați starea ciclului de viață înainte de operațiile cu FragmentManager. Utilizați commitAllowingStateLoss() doar când sunteți sigur că pierderea stării nu este critică. În Kotlin, creați constructori asemănători DSL care exclud stările incorecte la nivel de tipuri.
Native Crash apare în codul nativ C/C++ la încălcarea memoriei: acces prin pointer nul, double-free, depășirea bufferului de stivă. În Android, astfel de avarii apar în bibliotecile NDK, motoarele de jocuri (Unity, Unreal) și dependențele de sistem. Native Crash NU este interceptat de Thread.setDefaultUncaughtExceptionHandler — ucide procesul instantaneu.
Pentru diagnosticarea avariilor native, utilizați fișierele minidump (Breakpad) sau tombstone-urile Android. Firebase Crashlytics suportă colectarea avariilor native prin NDK SDK. Pe iOS, problema similară se rezolvă prin PLCrashReporter.
Trei instrumente domină piața de raportare a avariilor mobile. Fiecare oferă colectarea stack trace, agregarea pe versiuni de aplicație și notificări despre noile căderi.
Crashlytics — cel mai popular raportor de avarii pentru aplicații mobile, care face parte din ecosistemul Firebase. Acesta colectează automat stack trace, datele dispozitivului, versiunea sistemului de operare și cheile personalizate ale utilizatorului. Integrarea durează 10 minute prin Firebase Console și Gradle Plugin. Crashlytics suportă, de asemenea, jurnalele reale (Logcat) și urmărirea personalizată.
FirebaseCrashlytics.getInstance()
.setCustomKey("current_screen", "ProfileFragment")
FirebaseCrashlytics.getInstance()
.log("User tapped login button")
Sentry — o alternativă la Crashlytics cu un sistem de filtrare mai flexibil și suport pentru 90+ platforme. Spre deosebire de Firebase, Sentry oferă server auto-găzduit (self-hosted) pentru companiile cu cerințe stricte de date. Sentry suportă urmărirea distributivă, breadcrumbs și integrarea cu pipeline-uri CI/CD.
Bugsnag se remarcă prin suportul alertelor bazate pe severitate: împarte avariile în critice, erori și avertismente. AppCenter de la Microsoft — instrument gratuit cu funcționalitate de bază pentru proiecte mici. Ambele suportă Android, iOS, React Native și Flutter.
Analiza avariei este procesul de reconstituire a imaginii complete a evenimentului. Stack trace arată doar ultimul punct de eșec, dar nu oferă contextul care a dus la problemă. Abordarea profesională include patru etape.
Prima etapă — citirea stack trace. Identificați clasa, metoda și linia de cod unde a avut loc excepția. Urmați lanțul de apeluri de la cadrul de sus la cel de jos: ultima linie din stivă este locul avariei, iar liniile de sus sunt secvența apelurilor. Deobfuscarea (maparea ProGuard/R8) este obligatorie pentru build-urile de producție.
A doua etapă — contextul dispozitivului. Crashlytics arată modelul dispozitivului, versiunea sistemului de operare, memoria disponibilă și versiunea aplicației. De exemplu, o avarie doar pe Samsung Galaxy S10 cu Android 11 indică o problemă cu o versiune specifică de One UI, nu o eroare generală de cod.
A treia etapă — reproducerea pe un dispozitiv de test. Dacă avaria nu se reproduce stabil, întrebați utilizatorul despre pașii exacti sau utilizați Remote Config pentru jurnalizarea înaintea secțiunii problematice de cod. Testarea AB a remedierii pe o parte a audienței ajută la confirmarea soluției.
A patra etapă — monitorizarea după remediere. După publicarea remedierii, observați frecvența avariei timp de 3–5 zile. Dacă avaria a dispărut complet — remedierea a funcționat. Dacă frecvența a scăzut dar nu a ajuns la zero — există un al doilea scenariu care necesită o analiză separată.
Abordarea sistematică a prevenirii avariilor include instrumente de analiză statică, testarea obligatorie a cazurilor limită și gestionarea corectă a erorilor la toate nivelurile aplicației.
Detekt (Kotlin) și Lint (Android) găsesc probleme potențiale în faza de compilare: variabile neutilizate, NPE potențiale, utilizarea incorectă a API. Activați aceste instrumente în pipeline-ul CI cu un prag de erori. De exemplu, Detekt cu configurație de 30+ avertismente sau orice error-blocking nu lasă build-ul să treacă.
Acoperirea scenariilor cheie de utilizare cu teste unitare este protecția de bază împotriva avariilor de regresie. Testați modelele de date, ViewModel și straturile UseCase cu cazuri limită: valori null, liste goale, JSON incorect. Testele UI prin Espresso sau Compose Test acoperă fluxurile critice: autentificare, plată, onboarding.
Proiectați aplicația astfel încât o avarie într-un modul să nu doboare întregul ecran. Utilizați blocuri catch la nivelul ViewModel cu returnarea unei stări de rezervă: afișarea unui placeholder în locul listei, date din cache la lipsa rețelei, imagine de rezervă la eroarea de încărcare. Aceasta transformă o avarie potențială într-un scenariu UX controlat.
Staged rollouts — practica standard a Google Play și App Store: o nouă versiune este distribuită către 5%, apoi 20% și în final 100% din audiență cu un interval de 1–3 zile. În fiecare etapă se monitorizează frecvența avariilor: dacă rata crash-free scade sub 99.5%, lansarea se oprește automat. Firebase Remote Config permite dezactivarea funcțiilor problematice fără a publica o nouă versiune.
Renovate sau Dependabot în CI verifică automat bibliotecile pentru vulnerabilități cunoscute și erori critice. Actualizarea unei singure dependențe poate elimina o întreagă clasă de avarii. Cu toate acestea, testați actualizările în mediul de staging înainte de lansarea în producție — noua versiune a bibliotecii poate conține modificări incompatibile.
Întrebări frecvente
Nu. O parte din avarii sunt cauzate de factori în afara controlului dezvoltatorului: erori de sistem, probleme hardware, incompatibilitate de firmware. Scopul este reducerea frecvenței la 0.1% și mai jos, iar avariile rămase să fie minimizate din punct de vedere al timpului de reacție.
Raportorul de avarii colectează stack trace, starea memoriei și dispozitivul în momentul avariei. Analitica colectează date comportamentale ale utilizatorului. Crashlytics combină ambele abordări, oferind contextul avariei împreună cu cheile personalizate ale utilizatorului.
ProGuard și R8 ofuschează codul pentru protejarea proprietății intelectuale. Pentru deofuscare, încărcați fișierul de mapare în Crashlytics la publicare. Fără fișierul de mapare, stack trace va afișa a.a(), b.b() în locul numelor reale ale claselor și metodelor.
Prin Thread.setDefaultUncaughtExceptionHandler pe Android: biblioteca își înregistrează propriul handler, care primește primul excepția nemanipulată, salvează datele și abia apoi termină procesul. Pe iOS se folosește NSSetUncaughtExceptionHandler pentru NSException și Mach exception handler pentru semnale.
Fatal — aplicația s-a terminat. Non-fatal (excepție prinsă) — dezvoltatorul a prins excepția prin try-catch, dar aceasta poate indica o problemă potențială. Crashlytics distinge aceste tipuri și permite filtrarea non-fatal separat pentru a nu aglomera panoul de bord.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și