Πάγωμα στην ανάπτυξη — ουσία, αιτίες και αποτροπή

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

Πάγωμα (visnet) — είναι μια κατάσταση κατά την οποία η εφαρμογή κινητού σταματά να ανταποκρίνεται σε οποιεσδήποτε ενέργειες του χρήστη για μεγάλο χρονικό διάστημα. Σε αντίθεση με τα lag (επιβράδυνση) και τα glitch (λανθασμένη συμπεριφορά), το πάγωμα μπλοκάρει πλήρως το UI: τα αγγίγματα δεν επεξεργάζονται, η κίνηση σταματά, η οθόνη „παγώνει". Αιτία — μπλοκάρισμα του κύριου νήματος από σύγχρονη λειτουργία, deadlock σε πολυνηματικό κώδικα ή ανώμαλα μεγάλη συλλογή σκουπιδιών. Σύμφωνα με το Apple Main Thread Checker Documentation, πάνω από το 40% των αναφορών crash στο iOS σχετίζονται με μπλοκάρισμα του κύριου νήματος. Στο Android, ανάλογη κατάσταση οδηγεί σε ANR — το σύστημα διάλογο „Η εφαρμογή δεν ανταποκρίνεται".

Κύρια σημεία

  • Πάγωμα — πλήρες μπλοκάρισμα του UI για μεγάλο χρονικό διάστημα (δευτερόλεπτα και δεκάδες δευτερόλεπτα), διαφορετικό από τα lag και glitch
  • Κύριες αιτίες — μπλοκάρισμα του κύριου νήματος από είσοδο-έξοδο, deadlock μεταξύ νημάτων, ατέρμονος βρόχος και διαρροή μνήμης με μεγάλο GC
  • Διάγνωση περιλαμβάνει το Main Thread Checker στο iOS, αρχεία καταγραφής ANR /data/anr/traces.txt στο Android και ανάλυση dump νημάτων
  • Επιδιόρθωση — μεταφορά όλων των δυνητικά μεγάλων λειτουργιών σε νήματα παρασκηνίου, χρήση Structured Concurrency και αποφυγή synchronized στο νήμα UI
  • Πρόληψη — StrictMode, Main Thread Checker στο σχήμα Debug, στατική ανάλυση για deadlock και περιοδική εκτέλεση δοκιμών με μέτρηση χρόνου απόκρισης

Τι είναι το πάγωμα στην ανάπτυξη κινητών

Πάγωμα (freeze, hang) σε μια εφαρμογή κινητού — είναι μια κατάσταση κατά την οποία η εφαρμογή σταματά να επεξεργάζεται συμβάντα εισόδου και να ενημερώνει τη διεπαφή για αρκετά δευτερόλεπτα ή περισσότερο. Τεχνικά, αυτό σημαίνει ότι το κύριο νήμα (main thread) είναι μπλοκαρισμένο και δεν μπορεί να εκτελέσει τον επόμενο κύκλο του runner.

Διαφορά μεταξύ παγώματος, lag και ANR

Lag — είναι καθυστέρηση έως 500 ms, κατά την οποία ο χρήστης παρατηρεί επιβράδυνση, αλλά η εφαρμογή συνεχίζει να λειτουργεί. Πάγωμα διαρκεί από 1 δευτερόλεπτο έως δεκάδες δευτερόλεπτα. ANR στο Android — είναι μια ειδική περίπτωση παγώματος που διήρκεσε περισσότερο από 5 δευτερόλεπτα και εντοπίστηκε από το σύστημα. Δεν οδηγεί κάθε πάγωμα σε ANR, αλλά κάθε ANR είναι ένα τεκμηριωμένο πάγωμα από το σύστημα.

Συνέπειες παγώματος

Στο Android, πάγωμα μεγαλύτερο από 5 δευτερόλεπτα προκαλεί διάλογο ANR με πρόταση κλεισίματος της εφαρμογής. Στο iOS, το σύστημα διαθέτει watchdog — εάν η εφαρμογή δεν ανταποκρίνεται σε συμβάντα εντός 10–20 δευτερολέπτων, το Watchdog τερματίζει τη διεργασία με κωδικό 0x8badf00d (ate bad food). Ο χρήστης βλέπει μόνο το ξαφνικό κλείσιμο της εφαρμογής και την επιστροφή στην αρχική οθόνη.

Αιτίες παγώματος σε Android και iOS

Κάθε λειτουργία που διαρκεί περισσότερο από 100 ms και εκτελείται στο κύριο νήμα ενδέχεται να προκαλέσει πάγωμα. Ας εξετάσουμε τις κύριες πηγές μπλοκαρίσματος.

Σύγχρονη είσοδος-έξοδος στο νήμα UI

Ανάγνωση μεγάλου αρχείου, αίτημα δικτύου χωρίς ασυγχρονισμό, αποθήκευση δεδομένων στο SharedPreferences με σύγχρονη μέθοδο apply ακολουθούμενη από commit — όλες αυτές οι λειτουργίες μπλοκάρουν το κύριο νήμα. Στο Android, η σύγχρονη ανάγνωση αρχείου 10 MB μπορεί να διαρκέσει 200–500 ms ανάλογα με την ταχύτητα της μνήμης flash. Στο iOS, η σύγχρονη φόρτωση URLSession χωρίς completionHandler μπλοκάρει το UI για το χρόνο απόκρισης του διακομιστή.

Deadlock σε πολυνηματικό κώδικα

Όταν δύο νήματα περιμένουν την απελευθέρωση πόρων που κατέχονται αμοιβαία, προκύπτει deadlock. Σε εφαρμογές κινητών, το τυπικό σενάριο — το νήμα A μπλοκάρει το Lock1 και περιμένει το Lock2, ενώ το νήμα B μπλοκάρει το Lock2 και περιμένει το Lock1. Και τα δύο νήματα παγώνουν για πάντα. Εάν ένα από αυτά είναι το κύριο νήμα, η εφαρμογή παγώνει εντελώς.

Ατέρμονος βρόχος ή αναδρομή

Σφάλμα λογικής — για παράδειγμα, while(true) χωρίς συνθήκη εξόδου ή αναδρομή χωρίς βασική περίπτωση — οδηγεί σε ατέρμονη εκτέλεση στο κύριο νήμα. Android το εντοπίζει μέσω ANR μετά από 5 δευτερόλεπτα, iOS — μέσω Stackshot, το οποίο καταγράφει την ατέρμονα επαναλαμβανόμενη στοίβα κλήσεων.

  • Android — Cursor χωρίς κλείσιμο, σύγχρονο αίτημα μέσω execute() αντί για enqueue(), FileInputStream.read() στο νήμα UI
  • iOS — performSelectorOnMainThread:withObject:waitUntilDone:YES, εκτέλεση NSURLConnection sendSynchronousRequest, φόρτωση εικόνας με dataWithContentsOfURL
  • Cross-platform — Flutter compute χωρίς αποκλειστικό isolate, React Native σύγχρονο NativeModule

Πώς να διαγνώσετε το πάγωμα

Η διάγνωση του παγώματος απαιτεί εργαλεία ικανά να καταγράψουν την κατάσταση όλων των νημάτων τη στιγμή του μπλοκαρίσματος.

Αρχεία καταγραφής ANR στο Android

Σε κάθε ANR, το σύστημα Android αποθηκεύει το αρχείο /data/anr/traces.txt, το οποίο περιέχει dump στοίβας κάθε νήματος της εφαρμογής. Η ανάλυση αυτού του αρχείου — η κύρια μέθοδος διάγνωσης: πρέπει να βρεθεί το νήμα main και να δούμε σε ποια μέθοδο σταμάτησε. Εάν η στοίβα τελειώνει σε Thread.sleep, InputStream.read ή Lock.lock — η αιτία βρέθηκε.

Stackshot στο iOS

Xcode κατά το πάγωμα της εφαρμογής (σήμα SIGSTOP) μπορεί να λάβει Stackshot — στιγμιότυπο των στοιβών όλων των νημάτων. Ενεργοποιήστε στο σχήμα „Logging" → „Include Stackshot Logs". Σε crash με κωδικό 0x8badf00d, εξάγετε το αρχείο καταγραφής crash από το Devices & Simulators και βρείτε το νήμα com.apple.main-thread με παγωμένη στοίβα.

Main Thread Checker στο Xcode

Main Thread Checker εντοπίζει αυτόματα κλήσεις UIKit από νήματα παρασκηνίου κατά τη λειτουργία της εφαρμογής. Ενεργοποιήστε το στο σχήμα (Diagnostics → Main Thread Checker). Κάθε προειδοποίηση είναι πιθανή αιτία παγώματος, ειδικά εάν εμφανίζεται στο κλείσιμο completionHandler ενός αιτήματος δικτύου.

Παράδειγμα ανίχνευσης μπλοκαρίσματος μέσω StrictMode στο Android:

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

Μέθοδοι εξάλειψης μπλοκαρισμάτων UI

Η εξάλειψη του παγώματος ξεκινά με τη μεταφορά όλων των δυνητικά μεγάλων λειτουργιών σε νήματα παρασκηνίου. Ας εξετάσουμε συγκεκριμένες τεχνικές για κάθε πλατφόρμα.

Structured Concurrency με coroutines

Kotlin Coroutines με viewModelScope.launch(Dispatchers.IO) εγγυώνται ότι η λειτουργία δικτύου ή η ανάγνωση βάσης δεδομένων εκτελούνται σε νήμα παρασκηνίου. Το Dispatchers.Main χρησιμοποιείται μόνο για ενημέρωση του UI. Σημαντικό: όλες οι συναρτήσεις suspend πρέπει να είναι δομημένες — τα θυγατρικά coroutine ακυρώνονται κατά την ακύρωση του γονέα, αποτρέποντας διαρροή νήματος.

Ασύγχρονες ουρές στο iOS

Grand Central Dispatch με DispatchQueue.global(qos: .userInitiated) για εργασίες παρασκηνίου και DispatchQueue.main.async για ενημέρωση UI — το τυπικό μοτίβο. Αποφύγετε το sync() στην κύρια ουρά — αυτό είναι εγγυημένο deadlock. Χρησιμοποιήστε async/await (Swift 5.5+) για πιο ευανάγνωστο ασύγχρονο κώδικα με αυτόματη επιστροφή στο κύριο νήμα μέσω MainActor.

Αποφυγή synchronized στο νήμα UI

Τα μπλοκ synchronized στο Kotlin και το @synchronized στο Swift στο κύριο νήμα είναι επικίνδυνα: εάν ένα άλλο νήμα έχει ήδη αποκτήσει αυτό το κλείδωμα, το κύριο νήμα θα παγώσει αναμένοντας. Χρησιμοποιήστε ατομικούς τύπους (AtomicInteger, ατομικές ιδιότητες στο Swift) ή σειριακές ουρές αντί για κλειδώματα.

Παράδειγμα ασύγχρονης φόρτωσης δεδομένων με coroutine στο Android:

kotlin
class DataViewModel : ViewModel() {
    private val _data = MutableStateFlow<List<Item>>(emptyList())
    val data: StateFlow<List<Item>> = _data.asStateFlow()

    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchFromNetwork()
            _data.emit(result)
        }
    }
}

Πρόληψη παγώματος στο στάδιο ανάπτυξης

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

StrictMode με penaltyDeath

Ρυθμίστε το StrictMode με penaltyDeath για πολιτικές νημάτων — αυτό θα οδηγήσει σε άμεση κατάρρευση της εφαρμογής κατά τον εντοπισμό κλήσης δικτύου ή I/O δίσκου στο κύριο νήμα. Ο προγραμματιστής δεν μπορεί να αγνοήσει το πρόβλημα. Στην έκδοση παραγωγής, χρησιμοποιήστε penaltyLog για συλλογή στατιστικών χωρίς καταρρεύσεις.

Main Thread Checker στο σχήμα Debug

Στο iOS, ενεργοποιήστε το Main Thread Checker στο σχήμα Debug και ρυθμίστε το CI να εκτελεί δοκιμές με αυτήν την επιλογή. Εάν η δοκιμή περιέχει κλήση UIKit από νήμα παρασκηνίου — πρέπει να αποτύχει. Αυτός είναι ο μόνος αξιόπιστος τρόπος εντοπισμού του προβλήματος πριν από την αποστολή στο TestFlight.

Αναθεώρηση κώδικα με έλεγχο πολυνηματικότητας

Προσθέστε στη διαδικασία αναθεώρησης κώδικα ένα υποχρεωτικό σημείο: έλεγχος ότι κάθε κλήση δικτύου, εργασία με αρχεία, βάση δεδομένων ή βαριά υπολογιστική εργασία εκτελείται σε νήμα παρασκηνίου. Το deadlock μπορεί να εντοπιστεί με στατικό αναλυτή: το Infer από το Facebook και το Thread Safety Checker από το Xcode βρίσκουν πιθανά μπλοκαρίσματα πριν από την εκτέλεση.

  • Android — StrictMode, Infer, Android Lint Multithread, Kotlin Coroutines με viewModelScope
  • iOS — Main Thread Checker, TSAN (Thread Sanitizer), Xcode Analyze, Swift async/await με MainActor
  • Cross-platform — Flutter compute isolate, React Native interaction manager με requestAnimationFrame

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

Ποια είναι η διαφορά μεταξύ παγώματος και ANR;

ANR (Application Not Responding) — είναι μια ειδοποίηση συστήματος Android που εμφανίζεται όταν το κύριο νήμα παγώνει για περισσότερο από 5 δευτερόλεπτα. Το πάγωμα είναι ευρύτερη έννοια: οποιοδήποτε μπλοκάρισμα UI οποιασδήποτε διάρκειας. Στο iOS δεν υπάρχει ANR, αλλά υπάρχει Watchdog με χρονικό όριο 10–20 δευτερολέπτων.

Πώς να διαβάσετε το traces.txt στο Android;

Το αρχείο βρίσκεται στο /data/anr/traces.txt. Για πρόσβαση απαιτείται root ή adb shell: εκτελέστε adb shell cat /data/anr/traces.txt \> traces.txt με δικαιώματα root. Στη στοίβα, βρείτε το νήμα „main" — η τελευταία κληθείσα μέθοδος υποδεικνύει την αιτία του μπλοκαρίσματος.

Γιατί η εφαρμογή παγώνει στο iOS αλλά δεν καταρρέει;

Εάν το πάγωμα διαρκεί λιγότερο από 10 δευτερόλεπτα, το Watchdog δεν ενεργοποιείται και η εφαρμογή απλώς „παγώνει" έως ότου ολοκληρωθεί η λειτουργία μπλοκαρίσματος. Ο χρήστης δεν βλέπει κατάρρευση, αλλά βιώνει απογοήτευση. Για τον εντοπισμό τέτοιων περιπτώσεων, χρησιμοποιήστε το MetricKit με προσαρμοσμένες ιχνηλασίες χρόνου εκτέλεσης.

Πώς να δοκιμάσετε την εφαρμογή για πάγωμα;

Χρησιμοποιήστε δοκιμές UI με έλεγχο ότι η οθόνη ανοίγει σε λιγότερο από 1 δευτερόλεπτο. Προσθέστε στο CI μέτρηση χρόνου μεταξύ αγγίγματος και εμφάνισης της επόμενης οθόνης. Στο Android, χρησιμοποιήστε Espresso με IdlingResource για αναμονή ασύγχρονων λειτουργιών. Στο iOS, XCTest με XCTWaiter για έλεγχο χρόνου φόρτωσης.

Μπορεί το SwiftUI να προκαλέσει πάγωμα;

SwiftUI από μόνο του δεν προκαλεί πάγωμα, αλλά πολύπλοκοι υπολογισμοί στην ιδιότητα body — ναι. Εάν το body υπολογίζεται για 500 ms λόγω βαριών λειτουργιών, το UI παγώνει. Λύση — μεταφέρετε τους υπολογισμούς στο Task.detached και ενημερώστε το @State ασύγχρονα στον κύριο actor.

Σύνοψη

  • Πάγωμα — πλήρες μπλοκάρισμα του UI για δευτερόλεπτα και δεκάδες δευτερόλεπτα, που προκαλείται από μπλοκάρισμα του κύριου νήματος, deadlock ή ατέρμονο βρόχο
  • Διάγνωση — /data/anr/traces.txt στο Android, Stackshot και Main Thread Checker στο iOS
  • Κύριες αιτίες — σύγχρονη I/O, deadlock μεταξύ νημάτων, ατέρμονη αναδρομή, μεγάλο GC
  • Επιδιόρθωση — coroutine με σωστούς dispatcher, async/await με MainActor, μεταφορά όλων των λειτουργιών I/O σε νήματα παρασκηνίου
  • Πρόληψη — StrictMode με penaltyDeath, Main Thread Checker, στατική ανάλυση deadlock (Infer, TSAN)
  • Στο Android πάγωμα > 5 δευτ. = ANR; στο iOS > 10–20 δευτ. = Watchdog crash (0x8badf00d)
  • Σύσταση: ενεργοποιήστε το Thread Sanitizer στο σχήμα Debug και ρυθμίστε το CI να εκτελεί δοκιμές με TSAN για εντοπισμό data race και deadlock

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

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

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

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