Crash στην κινητή ανάπτυξη: τι είναι, τύποι και μέθοδοι αποτροπής

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-29 Χρόνος ανάγνωσης: 9 λεπ

Crash — η αναγκαστική τερματισμός της κινητής εφαρμογής λόγω μη διαχειριζόμενης εξαίρεσης ή θανατηφόρου σφάλματος συστήματος. Σύμφωνα με τα δεδομένα του Firebase Crashlytics, περίπου το 2% των χρηστών αντιμετωπίζουν καθημερινά σφάλματα, και κάθε σφάλμα μειώνει τη διατήρηση κατά 10–20%. Η κατανόηση των αιτιών και των μεθόδων αποτροπής σφαλμάτων είναι υποχρεωτική δεξιότητα για τον κινητό προγραμματιστή.

Κύρια σημεία

  • Crash — μη διαχειριζόμενη εξαίρεση που οδηγεί σε αναγκαστική τερματισμό της διεργασίας
  • NullPointerException — ο πιο συχνός τύπος σφάλματος σε εφαρμογές Java/Kotlin
  • Αναφορείς σφαλμάτων συλλέγουν stack trace, κατάσταση συσκευής και δεδομένα χρήστη
  • Firebase Crashlytics — τυπικό εργαλείο για παρακολούθηση σφαλμάτων στην κινητή ανάπτυξη
  • Αποτροπή περιλαμβάνει σωστή διαχείριση σφαλμάτων, δοκιμές και έλεγχο null-ασφάλειας

Τι είναι το Crash

Crash — είναι ο αναγκαστικός τερματισμός της εφαρμογής που προκαλείται από μη διαχειριζόμενη εξαίρεση ή θανατηφόρο σήμα συστήματος που δεν διαχειρίστηκε στον κώδικα της εφαρμογής. Όταν το σύστημα ή η εικονική μηχανή (JVM, ART) ανιχνεύσει θανατηφόρα κατάσταση — NullPointerException, IndexOutOfBoundsException, OutOfMemoryError — σταματά αμέσως τη διεργασία και την αφαιρεί από τη μνήμη. Ο χρήστης βλέπει το ξαφνικό κλείσιμο της εφαρμογής χωρίς καμία ειδοποίηση σφάλματος συστήματος. Σύμφωνα με την Google, εφαρμογές με ποσοστό crash-free κάτω από 99% χάνουν έως και 20% των ενεργών χρηστών τους ανά μήνα.

Στο Android ο μηχανισμός διαχείρισης σφαλμάτων διαφέρει από τα επιτραπέζια συστήματα. Αντί για παράθυρο εντοπισμού σφαλμάτων με stack trace, το Android απλά σκοτώνει τη διεργασία και δεν αποθηκεύει λεπτομερείς πληροφορίες. Η συλλογή πληροφοριών σχετικά με το σφάλμα είναι καθήκον βιβλιοθηκών τρίτων (Crashlytics, Sentry, Bugsnag), οι οποίες υποκλέπτουν εξαιρέσεις μέσω του Thread.setDefaultUncaughtExceptionHandler προτού τερματιστεί η διεργασία.

iOS χρησιμοποιεί παρόμοιο μηχανισμό με NSException και Mach exceptions για τη διαχείριση θανατηφόρων σφαλμάτων. Σε περίπτωση μη διαχειριζόμενης εξαίρεσης, το σύστημα τερματίζει την εφαρμογή και η αναφορά αποθηκεύεται ως αρχείο .crash. Η συλλογή σφαλμάτων στο iOS απαιτεί ενσωμάτωση με Crashlytics ή ενσωματωμένη αναφορά μέσω Xcode Organizer.

Κύριοι τύποι σφαλμάτων

Πέντε κατηγορίες σφαλμάτων καλύπτουν το 90% όλων των σφαλμάτων σε κινητές εφαρμογές. Η κατανόηση κάθε τύπου βοηθά στη γρηγορότερη διάγνωση και επιδιόρθωση προβλημάτων στο περιβάλλον παραγωγής.

NullPointerException — ο βασιλιάς των σφαλμάτων

NullPointerException (NPE) — ο πιο διαδεδομένος τύπος σφάλματος σε όλες τις εφαρμογές Java/Kotlin. Προκύπτει κατά την προσπάθεια κλήσης μεθόδου ή πρόσβασης σε πεδίο αντικειμένου που είναι null. Τυπικά σενάρια: μη αρχικοποιημένο πεδίο Activity κατά την περιστροφή οθόνης, απόκριση null από τον διακομιστή κατά την αποσειριοποίηση JSON, απρόσεκτη πλοήγηση μέσω του προσαρμογέα RecyclerView.

Το Kotlin λύνει το πρόβλημα NPE σε επίπεδο γλώσσας μέσω null-ασφαλών τύπων: το String? δεν μπορεί να χρησιμοποιηθεί χωρίς ρητό έλεγχο. Ωστόσο, η συμβατότητα με Java και η Reflection εξακολουθούν να δημιουργούν κινδύνους. Χρησιμοποιήστε σχολιασμούς @NonNull και @Nullable και ενεργοποιήστε το strictNullChecks στα εργαλεία στατικής ανάλυσης.

kotlin
fun safeLength(text: String?): Int {
    return text?.length ?: 0 // ασφαλής διαχείριση null
}

IndexOutOfBoundsException και σφάλματα συλλογών

IndexOutOfBoundsException προκύπτει κατά την πρόσβαση σε ανύπαρκτο δείκτη λίστας ή πίνακα. Συχνό σενάριο: αφαίρεση στοιχείου από RecyclerView χωρίς συγχρονισμό με τον προσαρμογέα, πολυνηματική τροποποίηση ArrayList χωρίς κλείδωμα, λανθασμένος υπολογισμός θέσης στο ViewPager. ConcurrentModificationException — στενός συγγενής κατά την ταυτόχρονη επανάληψη και τροποποίηση συλλογών.

Χρησιμοποιήστε CopyOnWriteArrayList για πολυνηματική πρόσβαση ή συλλογές Lock-free από το java.util.concurrent. Για συγχρονισμό με το UI, εφαρμόστε το DiffUtil, το οποίο υπολογίζει με ασφάλεια και αποδοτικότητα τη διαφορά μεταξύ παλιάς και νέας λίστας.

ClassCastException — τυπικά προβλήματα

ClassCastException προκύπτει κατά τη μετατροπή αντικειμένου σε ασύμβατο τύπο. Στο Android τυπικές αιτίες: λανθασμένος τύπος ViewHolder στο RecyclerView (διαφορετικοί τύποι κελιών χωρίς σωστό getItemViewType), λανθασμένη μετατροπή Fragment κατά την πλοήγηση, αντικείμενα Serializable με διαφορετικές εκδόσεις κλάσεων.

Χρησιμοποιήστε ασφαλή μετατροπή Kotlin μέσω του τελεστή as?, ο οποίος επιστρέφει null σε περίπτωση ασυμβατότητας τύπου. Στην Java — έλεγχος μέσω instanceof πριν από τη μετατροπή. Για αντικείμενα Parcelable, δηλώστε υποχρεωτικά CREATOR σε κάθε κλάση.

IllegalStateException και λογικά σφάλματα

IllegalStateException σηματοδοτεί την κλήση μεθόδου σε ακατάλληλη κατάσταση αντικειμένου. Τυπικό παράδειγμα στο Android — getSupportFragmentManager() μετά από onSaveInstanceState, όταν το commit() του fragment δεν επιτρέπεται. Άλλη συχνή περίπτωση — κλήση dismiss() σε ήδη κλειστό παράθυρο διαλόγου.

Ελέγξτε την κατάσταση του κύκλου ζωής πριν από λειτουργίες με FragmentManager. Χρησιμοποιήστε το commitAllowingStateLoss() μόνο όταν είστε σίγουροι ότι η απώλεια κατάστασης δεν είναι κρίσιμη. Στο Kotlin δημιουργήστε δομητές παρόμοιους με DSL που αποκλείουν λανθασμένες καταστάσεις σε επίπεδο τύπου.

Native Crash (σήματα SIGSEGV, SIGABRT)

Native Crash προκύπτει σε εγγενή κώδικα C/C++ κατά παραβίαση μνήμης: πρόσβαση μέσω null δείκτη, double-free, υπερχείλιση buffer στοίβας. Στο Android τέτοια σφάλματα συμβαίνουν σε βιβλιοθήκες NDK, μηχανές παιχνιδιών (Unity, Unreal) και εξαρτήσεις συστήματος. Το Native Crash ΔΕΝ υποκλέπτεται από το Thread.setDefaultUncaughtExceptionHandler — σκοτώνει τη διεργασία ακαριαία.

Για διάγνωση εγγενών σφαλμάτων χρησιμοποιήστε αρχεία minidump (Breakpad) ή tombstone Android. Το Firebase Crashlytics υποστηρίζει συλλογή εγγενών σφαλμάτων μέσω NDK SDK. Στο iOS, το παρόμοιο πρόβλημα λύνεται μέσω PLCrashReporter.

Εργαλεία αναφοράς σφαλμάτων

Τρία εργαλεία κυριαρχούν στην αγορά αναφοράς κινητών σφαλμάτων. Κάθε ένα παρέχει συλλογή stack trace, συγκέντρωση ανά έκδοση εφαρμογής και ειδοποιήσεις για νέα σφάλματα.

Firebase Crashlytics

Crashlytics — ο πιο δημοφιλής αναφοράς σφαλμάτων για κινητές εφαρμογές, μέρος του οικοσυστήματος Firebase. Συλλέγει αυτόματα stack trace, δεδομένα συσκευής, έκδοση λειτουργικού συστήματος και προσαρμοσμένα κλειδιά χρήστη. Η ενσωμάτωση διαρκεί 10 λεπτά μέσω Firebase Console και Gradle Plugin. Το Crashlytics υποστηρίζει επίσης πραγματικά αρχεία καταγραφής (Logcat) και προσαρμοσμένη παρακολούθηση.

kotlin
FirebaseCrashlytics.getInstance()
    .setCustomKey("current_screen", "ProfileFragment")

FirebaseCrashlytics.getInstance()
    .log("User tapped login button")

Sentry

Sentry — εναλλακτική λύση για το Crashlytics με πιο ευέλικτο σύστημα φιλτραρίσματος και υποστήριξη 90+ πλατφορμών. Σε αντίθεση με το Firebase, το Sentry παρέχει αυτο-φιλοξενούμενο διακομιστή (self-hosted) για εταιρείες με αυστηρές απαιτήσεις δεδομένων. Το Sentry υποστηρίζει διανεμητική παρακολούθηση, breadcrumbs και ενσωμάτωση με αγωγούς CI/CD.

Bugsnag και AppCenter

Bugsnag ξεχωρίζει με υποστήριξη ειδοποιήσεων βάσει σοβαρότητας: διαχωρίζει τα σφάλματα σε κρίσιμα, λάθη και προειδοποιήσεις. AppCenter από τη Microsoft — δωρεάν εργαλείο με βασική λειτουργικότητα για μικρά έργα. Και τα δύο υποστηρίζουν Android, iOS, React Native και Flutter.

Πώς να αναλύσετε ένα σφάλμα

Η ανάλυση σφάλματος είναι η διαδικασία ανακατασκευής της πλήρους εικόνας του συμβάντος. Το stack trace δείχνει μόνο το τελευταίο σημείο αποτυχίας, αλλά δεν παρέχει το πλαίσιο που οδήγησε στο πρόβλημα. Η επαγγελματική προσέγγιση περιλαμβάνει τέσσερα στάδια.

Πρώτο στάδιο — ανάγνωση του stack trace. Προσδιορίστε την κλάση, τη μέθοδο και τη γραμμή κώδικα όπου συνέβη η εξαίρεση. Ακολουθήστε την αλυσίδα κλήσεων από το πάνω πλαίσιο προς τα κάτω: η τελευταία γραμμή στη στοίβα είναι η θέση του σφάλματος, και οι πάνω γραμμές είναι η ακολουθία κλήσεων. Η αποαποκρυπτογράφηση (ProGuard/R8 mapping) είναι υποχρεωτική για εκδόσεις παραγωγής.

Δεύτερο στάδιο — πλαίσιο συσκευής. Το Crashlytics δείχνει το μοντέλο συσκευής, την έκδοση λειτουργικού συστήματος, τη διαθέσιμη μνήμη και την έκδοση εφαρμογής. Για παράδειγμα, σφάλμα μόνο σε Samsung Galaxy S10 με Android 11 υποδεικνύει πρόβλημα με συγκεκριμένη έκδοση One UI, όχι γενικό σφάλμα κώδικα.

Τρίτο στάδιο — αναπαραγωγή σε συσκευή δοκιμής. Εάν το σφάλμα δεν αναπαράγεται σταθερά, ρωτήστε τον χρήστη για ακριβή βήματα ή χρησιμοποιήστε το Remote Config για καταγραφή πριν από το προβληματικό τμήμα κώδικα. Η δοκιμή AB της επιδιόρθωσης σε μέρος του κοινού βοηθά στην επιβεβαίωση της λύσης.

Τέταρτο στάδιο — παρακολούθηση μετά την επιδιόρθωση. Μετά τη δημοσίευση της επιδιόρθωσης, παρατηρήστε τη συχνότητα του σφάλματος για 3–5 ημέρες. Εάν το σφάλμα εξαφανίστηκε εντελώς — η επιδιόρθωση λειτούργησε. Εάν η συχνότητα μειώθηκε αλλά δεν μηδενίστηκε — υπάρχει δεύτερο σενάριο που απαιτεί ξεχωριστή ανάλυση.

Πρακτικές αποτροπής σφαλμάτων

Συστηματική προσέγγιση για την αποτροπή σφαλμάτων περιλαμβάνει εργαλεία στατικής ανάλυσης, υποχρεωτική δοκιμή ακραίων περιπτώσεων και σωστή διαχείριση σφαλμάτων σε όλα τα επίπεδα της εφαρμογής.

Στατική ανάλυση κώδικα

Detekt (Kotlin) και Lint (Android) βρίσκουν πιθανά προβλήματα στη φάση μεταγλώττισης: μη χρησιμοποιούμενες μεταβλητές, πιθανά NPE, λανθασμένη χρήση API. Ενεργοποιήστε αυτά τα εργαλεία στον αγωγό CI με όριο σφαλμάτων. Για παράδειγμα, το Detekt με διαμόρφωση 30+ προειδοποιήσεων ή οποιοδήποτε error-blocking δεν επιτρέπει τη δημιουργία.

Δοκιμές μονάδας και δοκιμές UI

Η κάλυψη βασικών σεναρίων χρήσης με δοκιμές μονάδας είναι η βασική προστασία από σφάλματα παλινδρόμησης. Δοκιμάστε μοντέλα δεδομένων, ViewModel και επίπεδα UseCase με ακραίες περιπτώσεις: τιμές null, κενές λίστες, μη έγκυρο JSON. Οι δοκιμές UI μέσω Espresso ή Compose Test καλύπτουν κρίσιμες ροές: αυθεντικοποίηση, πληρωμή, ενσωμάτωση.

Graceful Degradation

Σχεδιάστε την εφαρμογή έτσι ώστε μια αποτυχία σε μια ενότητα να μην καταρρίπτει ολόκληρη την οθόνη. Χρησιμοποιήστε μπλοκ catch σε επίπεδο ViewModel με επιστροφή εφεδρικής κατάστασης: εμφάνιση placeholder αντί για λίστα, προσωρινά αποθηκευμένα δεδομένα όταν δεν υπάρχει δίκτυο, εφεδρική εικόνα σε σφάλμα φόρτωσης. Αυτό μετατρέπει ένα πιθανό σφάλμα σε ελεγχόμενο σενάριο UX.

Σταδιακή κυκλοφορία με παρακολούθηση

Staged rollouts — η τυπική πρακτική του Google Play και App Store: μια νέα έκδοση διανέμεται στο 5%, στη συνέχεια στο 20% και τελικά στο 100% του κοινού με μεσοδιάστημα 1–3 ημερών. Σε κάθε στάδιο παρακολουθείται η συχνότητα σφαλμάτων: εάν το ποσοστό crash-free πέσει κάτω από 99,5%, η κυκλοφορία σταματά αυτόματα. Το Firebase Remote Config επιτρέπει την απενεργοποίηση προβληματικών λειτουργιών χωρίς δημοσίευση νέας έκδοσης.

Έλεγχος εκδόσεων εξαρτήσεων

Renovate ή Dependabot στο CI ελέγχουν αυτόματα βιβλιοθήκες για γνωστές ευπάθειες και κρίσιμα σφάλματα. Η ενημέρωση μιας εξάρτησης μπορεί να εξαλείψει μια ολόκληρη κατηγορία σφαλμάτων. Ωστόσο, δοκιμάστε τις ενημερώσεις σε περιβάλλον staging πριν από την κυκλοφορία στην παραγωγή — η νέα έκδοση της βιβλιοθήκης μπορεί να περιέχει ασύμβατες αλλαγές.

Συχνές Ερωτήσεις

Μπορεί να αποτραπεί το 100% των σφαλμάτων;

Όχι. Ένα μέρος των σφαλμάτων προκαλείται από παράγοντες εκτός ελέγχου του προγραμματιστή: σφάλματα συστήματος, προβλήματα υλικού, ασυμβατότητα υλικολογισμικού. Στόχος είναι η μείωση της συχνότητας στο 0,1% και κάτω, και τα υπόλοιπα σφάλματα να ελαχιστοποιηθούν ως προς τον χρόνο αντίδρασης.

Σε τι διαφέρει ο αναφοράς σφαλμάτων από την αναλυτική;

Ο αναφοράς σφαλμάτων συλλέγει stack trace, κατάσταση μνήμης και συσκευή τη στιγμή του σφάλματος. Η αναλυτική συλλέγει δεδομένα συμπεριφοράς χρήστη. Το Crashlytics συνδυάζει και τις δύο προσεγγίσεις, παρέχοντας το πλαίσιο σφάλματος μαζί με προσαρμοσμένα κλειδιά χρήστη.

Γιατί το stack trace είναι συγκεκαλυμμένο;

ProGuard και R8 συγκαλύπτουν τον κώδικα για προστασία πνευματικής ιδιοκτησίας. Για αποαπόκρυψη, ανεβάστε το αρχείο mapping στο Crashlytics κατά τη δημοσίευση. Χωρίς αρχείο mapping, το stack trace θα εμφανίσει a.a(), b.b() αντί για πραγματικά ονόματα κλάσεων και μεθόδων.

Πώς υποκλέπτει ο αναφοράς σφαλμάτων εξαιρέσεις;

Μέσω Thread.setDefaultUncaughtExceptionHandler στο Android: η βιβλιοθήκη καταγράφει τον δικό της χειριστή, ο οποίος λαμβάνει πρώτος τη μη διαχειριζόμενη εξαίρεση, αποθηκεύει δεδομένα και μόνο τότε τερματίζει τη διεργασία. Στο iOS χρησιμοποιείται NSSetUncaughtExceptionHandler για NSException και Mach exception handler για σήματα.

Τι είναι το fatal και non-fatal σφάλμα;

Fatal — η εφαρμογή τερματίστηκε. Non-fatal (συλληφθείσα εξαίρεση) — ο προγραμματιστής συνέλαβε την εξαίρεση μέσω try-catch, αλλά αυτό μπορεί να υποδεικνύει πιθανό πρόβλημα. Το Crashlytics διακρίνει αυτούς τους τύπους και επιτρέπει το φιλτράρισμα non-fatal ξεχωριστά για να μην γεμίζει τον πίνακα ελέγχου.

Περίληψη

  • Crash — αναγκαστικός τερματισμός εφαρμογής λόγω μη διαχειριζόμενης εξαίρεσης ή θανατηφόρου σήματος
  • NullPointerException παραμένει ο πιο συχνός τύπος σφάλματος σε κινητές εφαρμογές
  • Firebase Crashlytics — τυπικό εργαλείο για συλλογή και ανάλυση σφαλμάτων στην παραγωγή
  • Ανάλυση σφάλματος περιλαμβάνει ανάγνωση stack trace, πλαίσιο συσκευής και αναπαραγωγή σε περιβάλλον δοκιμής
  • Στατική ανάλυση (Detekt, Lint) αποτρέπει μέρος των σφαλμάτων στη φάση μεταγλώττισης
  • Graceful degradation μετατρέπει πιθανά σφάλματα σε διαχειρίσιμα σενάρια με εφεδρικά δεδομένα
  • Αρχεία mapping είναι υποχρεωτικά για αποαπόκρυψη stack trace σε εκδόσεις παραγωγής

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης