ANR (Application Not Responding) — είναι μια συστημική ειδοποίηση Android που εμφανίζεται όταν η εφαρμογή σταματά να ανταποκρίνεται στην είσοδο του χρήστη για περισσότερο από 5 δευτερόλεπτα. Σύμφωνα με το Android Developers, η κύρια αιτία είναι οι μεγάλες λειτουργίες στο κύριο νήμα που μπλοκάρουν την επεξεργασία αφής και την απόδοση της διεπαφής. Η κατανόηση των μηχανισμών ANR είναι απαραίτητη για κάθε προγραμματιστή Android προκειμένου να δημιουργεί αποκριτικές εφαρμογές.
Κύρια σημεία
ANR (Application Not Responding) — είναι ένα παράθυρο διαλόγου του λειτουργικού συστήματος Android που εμφανίζεται όταν η εφαρμογή σταματά να ανταποκρίνεται στην είσοδο του χρήστη. Το σύστημα παρακολουθεί τον χρόνο επεξεργασίας συμβάντων μέσω του InputDispatcher: εάν ένα άγγιγμα ή πάτημα κουμπιού δεν υποβληθεί σε επεξεργασία εντός 5 δευτερολέπτων, το Android εμφανίζει ένα παράθυρο διαλόγου με την πρόταση να κλείσετε ή να περιμένετε την εφαρμογή.
Ο μηχανισμός ANR προστατεύει την εμπειρία χρήστη από εφαρμογές που έχουν παγώσει. Το Android δεν επιτρέπει σε μία εφαρμογή να μπλοκάρει ολόκληρο το σύστημα — σε αντίθεση με τα Desktop-OS, η κινητή πλατφόρμα περιορίζει υποχρεωτικά τον χρόνο επεξεργασίας συμβάντων. Το BroadcastReceiver έχει όριο 10 δευτερόλεπτα και η foreground υπηρεσία 20 δευτερόλεπτα.
Το ANR ΔΕΝ είναι εξαίρεση στον κώδικα — είναι ένας συστημικός μηχανισμός σε επίπεδο διεργασιών Linux. Το Android στέλνει σήμα SIGQUIT στη διεργασία, μετά το οποίο το σύστημα αποθηκεύει τη στοίβα κλήσεων όλων των νημάτων στο αρχείο traces.txt. Ο προγραμματιστής λαμβάνει το ANR όχι ως catch-εξαίρεση, αλλά ως αναφορά μετά από επανεκκίνηση της εφαρμογής. Από Android 11+ υπάρχει το API ApplicationExitInfo που επιτρέπει την προγραμματική λήψη της αιτίας τερματισμού της διεργασίας, συμπεριλαμβανομένου του ANR — αυτό απλοποιεί τη συλλογή στατιστικών χωρίς χειροκίνητη ανάλυση του traces.txt.
Πέντε κατηγορίες λειτουργιών οδηγούν σταθερά σε ANR σε εφαρμογές Android. Κάθε μία από αυτές μπλοκάρει το κύριο νήμα, εμποδίζοντας το σύστημα να επεξεργαστεί συμβάντα εισόδου και να ανανεώσει την οθόνη.
Σύγχρονα HTTP αιτήματα που εκτελούνται στο UI-νήμα είναι η πιο συνηθισμένη αιτία ANR σε αρχάριους προγραμματιστές. Ακόμη και ένα γρήγορο αίτημα στον διακομιστή μπορεί να διαρκέσει 1–3 δευτερόλεπτα, και με κακή σύνδεση — 30 δευτερόλεπτα ή περισσότερο. Το Android απαγορεύει ρητά τις δικτυακές λειτουργίες στο κύριο νήμα από το API 11, εκτοξεύοντας NetworkOnMainThreadException.
Χρησιμοποιήστε Coroutines ή RxJava για ασύγχρονες κλήσεις. Οι κορουτίνες με τον dispatcher Dispatchers.IO εκτελούν το αίτημα σε background νήμα και μεταφέρουν το αποτέλεσμα στο κύριο μέσω Dispatchers.Main. Αυτό εξαλείφει εντελώς το μπλοκάρισμα του UI-νήματος από δικτυακές λειτουργίες.
fun fetchUserData() {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
api.getUserData() // λειτουργία παρασκηνίου
}
updateUI(result) // αποτέλεσμα στο κύριο νήμα
}
}
Επεξεργασία μεγάλων συνόλων δεδομένων, ανάλυση JSON ή XML, εργασία με bitmap απευθείας στο κύριο νήμα — η δεύτερη πιο συχνή αιτία ANR. Ακόμη και 300 χιλιοστά του δευτερολέπτου συνεχούς εργασίας του UI-νήματος χωρίς επιστροφή στον βρόχο συμβάντων προκαλούν αισθητή καθυστέρηση απόδοσης, και η οριακή τιμή των 5 δευτερολέπτων καταγράφεται ως ANR.
WorkManager και background υπηρεσίες προορίζονται για τη μεταφορά βαρέων υπολογισμών εκτός κύριου νήματος. Χρησιμοποιήστε AsyncTask (ξεπερασμένο), ListenableFuture ή Kotlin Flow για μεταφορά δεδομένων σε τμήματα, χωρίς μπλοκάρισμα του UI.
Deadlock προκύπτει όταν δύο νήματα κρατούν κλειδώματα και περιμένουν το ένα το άλλο. Εάν ένα από τα νήματα είναι το κύριο, το σύστημα καταγράφει ANR ακριβώς μετά από 5 δευτερόλεπτα. Τα Thread.join(), CountDownLatch.await() και synchronized-μπλοκ που καλούνται από το UI-νήμα ενέχουν κίνδυνο μπλοκαρίσματος.
Αποφύγετε οποιεσδήποτε μπλοκάροντας λειτουργίες στο κύριο νήμα. Αντί για synchronized χρησιμοποιήστε ConcurrentHashMap, αντί για Thread.join() — κορουτίνες με async/await. Αυτός ο κανόνας ισχύει για οποιαδήποτε γλώσσα στο Android: Java, Kotlin ή C++ μέσω JNI.
BroadcastReceiver εκτελείται στο κύριο νήμα από προεπιλογή. Εάν το onReceive() είναι απασχολημένο για περισσότερο από 10 δευτερόλεπτα, το Android εμφανίζει ANR. Η φόρτωση δεδομένων από τη βάση δεδομένων ή το δίκτυο μέσα στο onReceive είναι εγγυημένος δρόμος προς το πάγωμα.
Χρησιμοποιήστε goAsync() μέσα στο BroadcastReceiver για εναλλαγή σε background νήμα, ή registerReceiver με getBackgroundBroadcastReceiver(). Αυτό επιτρέπει την επεξεργασία συμβάντων χωρίς μπλοκάρισμα του UI.
Βαριά αιτήματα στο ContentProvider ή άμεση εργασία με SQLite στο UI-νήμα — λιγότερο προφανής αλλά συχνή αιτία ANR. Κατά τη μετεγκατάσταση βάσης δεδομένων ή μαζική εισαγωγή χιλιάδων εγγραφών, ο χρόνος εκτέλεσης μπορεί να υπερβεί το όριο των 5 δευτερολέπτων.
Μεταφέρετε όλες τις λειτουργίες με τη βάση δεδομένων σε background νήματα μέσω Room με suspend-συναρτήσεις. Το Room ελέγχει αυτόματα ότι το αίτημα δεν εκτελείται στο κύριο νήμα και εκτοξεύει εξαίρεση σε περίπτωση παραβίασης.
Η διάγνωση του ANR διαφέρει από τον εντοπισμό συνήθων εξαιρέσεων — δεν μπορείτε να πιάσετε το ANR σε try-catch. Η κύρια πηγή πληροφοριών είναι το αρχείο traces.txt, το οποίο δημιουργεί το Android τη στιγμή του παγώματος.
traces.txt περιέχει τη στοίβα κλήσεων όλων των νημάτων της εφαρμογής τη στιγμή του ANR. Για να διαβάσετε το αρχείο από μια πραγματική συσκευή, εκτελέστε την εντολή adb bugreport, η οποία συλλέγει πλήρη αναφορά συστήματος, συμπεριλαμβανομένων όλων των ANR του τελευταίου χρονικού διαστήματος. Για τον εξομοιωτή, το αρχείο είναι διαθέσιμο στο /data/anr/traces.txt. Η στοίβα κλήσεων δείχνει ποια μέθοδος εκτελούνταν στο κύριο νήμα τη στιγμή του μπλοκαρίσματος.
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt
Google Play Console παρέχει την ενότητα ANR & Crash με συγκεντρωτικές αναφορές και συχνότητες σφαλμάτων. Για κάθε ANR εμφανίζεται η στοίβα κλήσεων και στατιστικά ανά συσκευή: μοντέλο, έκδοση Android, περιοχή. Αυτό επιτρέπει τον εντοπισμό ANR που εξαρτώνται από συγκεκριμένες συσκευές ή εκδόσεις συστήματος.
Το Android Studio από το 2021 περιέχει ANR Watchdog στον προφιλοποιητή. Καταγράφει αυτόματα dump νημάτων εάν το κύριο νήμα δεν ανταποκρίνεται για περισσότερο από το καθορισμένο χρονικό όριο. Το εργαλείο δείχνει μια χρονογραμμή συμβάντων: ποιες λειτουργίες ξεκίνησαν, ποιες μέθοδοι εκτελέστηκαν και σε ποιο στάδιο συνέβη το μπλοκάρισμα.
Η πρόληψη του ANR βασίζεται σε έναν θεμελιώδη κανόνα: το κύριο νήμα πρέπει να επεξεργάζεται μόνο συμβάντα UI. Οποιαδήποτε λειτουργία διαρκεί περισσότερο από 16 χιλιοστά του δευτερολέπτου (χρόνος ενός καρέ) πρέπει να εκτελείται σε background νήμα.
StrictMode — ενσωματωμένο εργαλείο Android για τον εντοπισμό πιθανών ANR κατά το στάδιο ανάπτυξης. Ενεργοποιήστε το στο Application.onCreate() με σημαίες για λειτουργίες δίσκου και δικτύου. Σε περίπτωση παραβίασης, το StrictMode εκτοξεύει εξαίρεση ή γράφει στο logcat.
if (BuildConfig.DEBUG) {
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
.let { StrictMode.setThreadPolicy(it) }
}
Kotlin Coroutines — ο τυπικός τρόπος ασύγχρονης εργασίας σε σύγχρονες εφαρμογές Android. Βασική τεχνική: οι λειτουργίες εισόδου/εξόδου εκτελούνται στο Dispatchers.IO, το αποτέλεσμα μεταφέρεται στο Dispatchers.Main για ενημέρωση του UI. Για σενάρια τύπου Flow, χρησιμοποιήστε Dispatchers.Default για εργασίες έντασης CPU.
RxJava παραμένει δημοφιλές σε παλαιότερα έργα. Το subscribeOn(Schedulers.io()) και observeOn(AndroidSchedulers.mainThread()) είναι το ελάχιστο σύνολο για την πρόληψη ANR. Ο ίδιος κανόνας ισχύει: κανένα Observable ή Flowable δεν πρέπει να εκπέμπει δεδομένα από το κύριο νήμα.
Firebase Crashlytics από την έκδοση SDK 18.4.0 υποστηρίζει παρακολούθηση ANR «εκτός κουτιού». Για Android 11+ το Crashlytics χρησιμοποιεί το σύστημα API ApplicationExitInfo, το οποίο παρέχει την ακριβή αιτία τερματισμού: ANR, Crash ή συστημική δολοφονία. Ενεργοποιήστε custom κλειδιά με παραμέτρους οθόνης και κατάστασης για συμφραζόμενη ανάλυση.
Πέντε εργαλεία καλύπτουν όλα τα στάδια εργασίας με ANR: από τον εντοπισμό στο workstation μέχρι την παρακολούθηση σε παραγωγή. Κάθε εργαλείο λύνει το δικό του πρόβλημα και παρέχει δεδομένα για διαφορετικά σενάρια.
| Εργαλείο | Σκοπός | Μορφή δεδομένων |
|---|---|---|
| StrictMode | Εντοπισμός στο στάδιο ανάπτυξης | Logcat / Exception |
| ANR Watchdog (Android Studio) | Ιχνηλάτηση σε πραγματικό χρόνο | Thread dump + timeline |
| Google Play Console | Συγκεντρωτικά στατιστικά | ANR rate + stack traces |
| Firebase Crashlytics | Παρακολούθηση παραγωγής | ApplicationExitInfo |
| adb bugreport | Πλήρης αναφορά συστήματος | traces.txt + logcat + dmesg |
Κάθε εργαλείο έχει τη δική του θέση: το StrictMode πιάνει προφανείς παραβιάσεις σε πρώιμα στάδια, το Crashlytics δείχνει την πραγματική συχνότητα ANR στους χρήστες, και το adb bugreport δίνει την πιο πλήρη εικόνα για σύνθετες περιπτώσεις. Συνδυάστε τα για πλήρη κάλυψη.
Firebase Performance παρακολουθεί τον χρόνο απόκρισης του UI-νήματος και δημιουργεί αυτόματα traces για ύποπτα μεγάλες λειτουργίες. Εάν το κύριο νήμα μπλοκαριστεί για περισσότερο από 500 ms, το Performance καταγράφει ένα custom trace με το όνομα της μεθόδου-υπαίτιου. Αυτό επιτρέπει τον εντοπισμό σεναρίων ANR χωρίς συμμετοχή του χρήστη και προτού γίνουν κρίσιμα.
Η ενσωμάτωση με το Firebase Crashlytics δίνει πλήρη εικόνα: το Performance δείχνει επιβραδύνσεις πριν το ANR και το Crashlytics — το ίδιο το γεγονός παγώματος. Ρυθμίστε ειδοποιήσεις στην Firebase Console για γεγονός ANR rate πάνω από 0.1% και θα λαμβάνετε ειδοποιήσεις για νέα προβλήματα πριν από μαζικά παράπονα χρηστών.
Συχνές ερωτήσεις
ANR — είναι ένα πάγωμα κατά το οποίο η εφαρμογή δεν ανταποκρίνεται αλλά παραμένει στη μνήμη. Crash — είναι πλήρης απροσδόκητος τερματισμός με έξοδο από τη διεργασία. Το ANR μπορεί να «επιβιώσει» εάν το σύστημα ή ο χρήστης περιμένουν απάντηση, ενώ το Crash τερματίζει πάντα την εφαρμογή.
Όχι. Το ANR δεν είναι εξαίρεση Java/Kotlin, αλλά σήμα συστήματος σε επίπεδο διεργασιών (SIGQUIT). Ο προγραμματιστής δεν μπορεί να το χειριστεί στον κώδικα της εφαρμογής. Ο μόνος τρόπος να αντιδράσετε στο ANR είναι να αναλύετε αναφορές μετά από επανεκκίνηση.
Η απόδοση της συσκευής, η έκδοση Android, ο φόρτος CPU και ο αριθμός των διεργασιών παρασκηνίου επηρεάζουν την πιθανότητα ANR. Σε αδύναμες συσκευές η ίδια λειτουργία μπορεί να εκτελείται 2–3 φορές περισσότερο, υπερβαίνοντας το όριο των 5 δευτερολέπτων.
10 δευτερόλεπτα για κανονικό BroadcastReceiver στο onReceive(). Για foreground υπηρεσίες το όριο είναι 20 δευτερόλεπτα, και για ContentProvider δεν υπάρχει σαφές όριο, αλλά το μπλοκάρισμα του κύριου νήματος για περισσότερο από 5 δευτερόλεπτα εξακολουθεί να προκαλεί ANR.
Ενεργοποιήστε το StrictMode σε όλες τις debug-εκδόσεις, προσθέστε παρακολούθηση μέσω Firebase Crashlytics και χρησιμοποιήστε adb bugreport όταν συμβαίνει ANR. Τα μη τακτικά ANR συχνά σχετίζονται με συνθήκες ανταγωνισμού (race condition) ή συγκεκριμένες καταστάσεις δικτύου.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης