ANR (Application Not Responding) — είναι μια ειδοποίηση συστήματος Android που εμφανίζεται όταν η εφαρμογή δεν ανταποκρίνεται στην είσοδο για 5 δευτερόλεπτα. Σε αντίθεση με τα glitch (λογικά σφάλματα χωρίς μπλοκάρισμα του UI) και τα lag (επιβράδυνση χωρίς πλήρη διακοπή), το ANR είναι μια κρίσιμη βλάβη που καταγράφεται από το λειτουργικό σύστημα: το Android εμφανίζει το παράθυρο διαλόγου «Η εφαρμογή δεν αποκρίνεται» με πρόταση να κλείσει ή να περιμένετε. Σύμφωνα με το Android Vitals Documentation, οι εφαρμογές με ποσοστό ANR άνω του 0,5% έχουν χαμηλότερη βαθμολογία στο Google Play και ενδέχεται να κρυφτούν από τις προτάσεις. Η διάγνωση περιλαμβάνει ανάλυση του /data/anr/traces.txt, χρήση του StrictMode και προφίλ του κύριου νήματος.
Κύρια σημεία
ANR (Application Not Responding) — είναι ένας μηχανισμός προστασίας του χρήστη στο Android που ενεργοποιείται όταν η εφαρμογή σταματά να ανταποκρίνεται στην είσοδο. Το σύστημα παρακολουθεί τον χρόνο επεξεργασίας συμβάντων: εάν το BroadcastReceiver δεν ολοκληρώσει το onReceive εντός 10 δευτερολέπτων, το Service δεν επιστρέψει από το onCreate εντός 20 δευτερολέπτων και το ContentProvider δεν απαντήσει εντός 15 δευτερολέπτων — το Android δημιουργεί ANR.
Όταν συμβαίνει ANR, το Android εμφανίζει ένα παράθυρο διαλόγου συστήματος πάνω από όλα τα παράθυρα: «Η εφαρμογή δεν αποκρίνεται. Να κλείσει ή να περιμένετε;». Ο χρήστης μπορεί να κλείσει την εφαρμογή ή να περιμένει την ανάκτησή της. Εάν τα ANR επαναλαμβάνονται συχνά, ο χρήστης διαγράφει την εφαρμογή. Το Google Play λαμβάνει υπόψη το ποσοστό ANR — το ποσοστό συνεδριών με ANR — στους αλγόριθμους κατάταξης.
Στο iOS δεν υπάρχει αντίστοιχο του ANR με παράθυρο διαλόγου συστήματος. Αντίθετα, η Apple χρησιμοποιεί το Watchdog, το οποίο τερματίζει τη διαδικασία της εφαρμογής με κωδικό 0x8badf00d. Ο χρήστης δεν βλέπει παράθυρο διαλόγου — η εφαρμογή απλά κλείνει στην αρχική οθόνη. Αυτό καθιστά το ANR στο Android πιο ορατό στον χρήστη, αλλά δίνει στο σύστημα περισσότερες πληροφορίες για διάγνωση.
Το ANR συμβαίνει όταν το σύστημα παρακολουθεί χρονικό όριο για έναν από τους τέσσερις τύπους στοιχείων. Κάθε στοιχείο έχει το δικό του χρονικό όριο.
BroadcastReceiver εκτελείται στο κύριο νήμα. Εάν το onReceive ξεκινήσει ένα σύγχρονο αίτημα δικτύου, μια μεγάλη λειτουργία εγγραφής στη βάση δεδομένων ή αναμονή για μπλοκάρισμα — μετά από 10 δευτερόλεπτα εμφανίζεται ANR. Λύση: χρησιμοποιήστε goAsync() και WorkManager για επεξεργασία στο παρασκήνιο. Τυπικό σενάριο — λήψη ειδοποίησης Push από το FCM και σύγχρονη αποθήκευση στο Room.
Service.onCreate και Service.onStartCommand έχουν όριο 20 δευτερολέπτων. Εάν η υπηρεσία ξεκινήσει βαριά αρχικοποίηση (φόρτωση βιβλιοθηκών, ανάγνωση παραμέτρων από το δίκτυο) στο κύριο νήμα — το ANR είναι αναπόφευκτο. Χρησιμοποιήστε IntentService (ξεπερασμένο) ή WorkManager για εγγυημένη εκτέλεση στο νήμα παρασκηνίου.
ContentProvider.onCreate εκτελείται πριν από την κλήση του Application.onCreate και έχει όριο 15 δευτερολέπτων. Εάν ο πάροχος εκτελεί μετεγκατάσταση βάσης δεδομένων, φόρτωση λεξικών ή αρχικοποίηση SDK από το δίκτυο — αυτό προκαλεί ANR κατά την εκκίνηση της εφαρμογής. Λύση: τεμπέλικη αρχικοποίηση, μεταφορά βαριών λειτουργιών στο WorkManager.
Το Android παρέχει διάφορα εργαλεία για ανάλυση ANR: από αρχεία καταγραφής συστήματος έως εξειδικευμένες βιβλιοθήκες.
Σε κάθε ANR, το Android αποθηκεύει το αρχείο /data/anr/traces.txt με απόρριψη στοίβας όλων των νημάτων της εφαρμογής. Βρείτε το νήμα «main» — η τελευταία μέθοδος στη στοίβα υποδεικνύει την αιτία. Τυπικά μοτίβα: Thread.sleep(), InputStream.read(), BinderProxy.transact(). Για εξαγωγή του αρχείου από τη συσκευή, χρησιμοποιήστε adb με δικαιώματα υπερχρήστη.
Firebase Crashlytics συλλέγει αυτόματα ANR και τα εμφανίζει στον πίνακα ελέγχου μαζί με ιχνηλάτηση. Για Android 11+, οι αναφορές ANR έρχονται με πλήρη στοίβα του κύριου νήματος. Η ενοποίηση απαιτεί προσθήκη εξάρτησης και αρχικοποίηση του FirebaseApp στο Application.onCreate.
CPU Profiler στο Android Studio σας επιτρέπει να καταγράψετε ιχνηλάτηση της λειτουργίας της εφαρμογής και να δείτε ποιες μέθοδοι καταλαμβάνουν χρόνο CPU. Ενεργοποιήστε το «Record with method traces» και αναπαράγετε το σενάριο που προκαλεί ANR. Στη γραμμή χρόνου θα είναι ορατό ποιες μέθοδοι εκτελούνταν στο κύριο νήμα τη στιγμή του παγώματος.
Παράδειγμα ενοποίησης Firebase Crashlytics για συλλογή ANR στο Android:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
Η αντιμετώπιση του ANR σημαίνει πρωτίστως μεταφορά όλων των μεγάλων λειτουργιών από το κύριο νήμα στα νήματα παρασκηνίου. Ας εξετάσουμε συγκεκριμένες τεχνικές για κάθε τύπο στοιχείου.
WorkManager — η συνιστώμενη λύση της Google για εργασία στο παρασκήνιο. Εγγυάται την εκτέλεση της εργασίας στο νήμα παρασκηνίου λαμβάνοντας υπόψη την κατάσταση της συσκευής. Σε αντίθεση με το Service, το WorkManager δεν μπλοκάρει το κύριο νήμα και είναι ανθεκτικό στην επανεκκίνηση της εφαρμογής. Για BroadcastReceiver, χρησιμοποιήστε goAsync() και μεταβιβάστε το αποτέλεσμα PendingResult στο WorkManager.
Εκκινήστε όλα τα αιτήματα δικτύου, την εργασία με βάση δεδομένων και τις λειτουργίες αρχείων με Dispatchers.IO. Το κύριο νήμα πρέπει μόνο να ενημερώνει το UI. Χρησιμοποιήστε viewModelScope για αυτόματη ακύρωση coroutine κατά την καταστροφή του Activity. Αποφύγετε το runBlocking() σε οποιοδήποτε περιβάλλον — αυτό είναι σύγχρονο μπλοκάρισμα του τρέχοντος νήματος.
Εάν το ContentProvider εκτελεί μεγάλη αρχικοποίηση, χρησιμοποιήστε μηχανισμό καθυστερημένης φόρτωσης: δημιουργήστε έναν πάροχο που επιστρέφει άμεσα δεδομένα και εκκινήστε τη βαριά αρχικοποίηση μέσω WorkManager με καθυστέρηση. Αυτό αποτρέπει το ANR κατά την εκκίνηση της εφαρμογής, όταν το σύστημα είναι πιο ευαίσθητο σε καθυστερήσεις.
Παράδειγμα σωστής χρήσης BroadcastReceiver με goAsync στο Android:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
Ο καλύτερος τρόπος αντιμετώπισης του ANR είναι η πρόληψη της εμφάνισής τους στο στάδιο ανάπτυξης μέσω εργαλείων και αρχιτεκτονικών λύσεων.
StrictMode με ενεργοποιημένες πολιτικές detectNetwork() και detectDiskReads()/detectDiskWrites() ανιχνεύει πιθανά ANR στο στάδιο ανάπτυξης. Στην έκδοση Debug, ορίστε penaltyDeath — οποιαδήποτε παραβίαση θα οδηγήσει σε άμεση κατάρρευση και ο προγραμματιστής θα δει το πρόβλημα πριν από την υποβολή.
Firebase Performance παρακολουθεί τον χρόνο εκτέλεσης βασικών λειτουργιών και δείχνει ποια σενάρια υπερβαίνουν το όριο ANR. Διαμορφώστε προσαρμοσμένες ιχνηλατήσεις για κάθε οθόνη και αίτημα δικτύου. Εάν ο χρόνος εκτέλεσης υπερβαίνει τα 3 δευτερόλεπτα — αυτό είναι ένα πιθανό ANR που απαιτεί βελτιστοποίηση.
Προσομοιώστε αργές συνθήκες: περιορίστε την ταχύτητα δικτύου μέσω του Network Link Conditioner στο iOS ή στο Android Emulator. Επιβραδύνετε την ανάγνωση από τον δίσκο μέσω εξομοίωσης αργής μνήμης. ANR εμφανίζεται συχνά ακριβώς σε τέτοιες συνθήκες, στις γρήγορες συσκευές του προγραμματιστή δεν είναι ορατό.
Συχνές Ερωτήσεις
Android παρακολουθεί ρητά τον χρόνο επεξεργασίας συμβάντων στο κύριο νήμα και εμφανίζει το παράθυρο διαλόγου ANR. Το iOS χρησιμοποιεί Watchdog, το οποίο κλείνει αναγκαστικά την εφαρμογή όταν παγώνει για περισσότερο από 10–20 δευτερόλεπτα. Το ANR είναι χαρακτηριστικό της αρχιτεκτονικής Android, όπου πολλά στοιχεία (BroadcastReceiver, Service) έχουν αυστηρά χρονικά όρια.
Σε Android 11+ μπορείτε να λάβετε απόρριψη ANR μέσω adb shell dumpsys dropbox --print data_app_anr. Σε Android 10 και παλαιότερα χωρίς root δεν υπάρχει πρόσβαση στο /data/anr/traces.txt. Χρησιμοποιήστε Firebase Crashlytics — συλλέγει αυτόματα αναφορές ANR για Android 11+.
Το Google Play συνιστά ποσοστό ANR κάτω από 0,5% — δηλαδή όχι περισσότερα από 5 ANR ανά 1000 συνεδρίες. Η εφαρμογή με ποσοστό άνω του 1% λαμβάνει προειδοποίηση στο Google Play Console και μπορεί να κρυφτεί από τις προτάσεις. Ιδανικά, το ποσοστό ANR θα πρέπει να είναι κάτω από 0,1%.
Το coroutine από μόνο του δεν μπλοκάρει το νήμα. Αλλά εάν μέσα στο coroutine εκτελείται runBlocking στο κύριο νήμα ή το coroutine εκκινείται με Dispatchers.Main και εκτελεί μεγάλη λειτουργία CPU — αυτό θα προκαλέσει ANR. Χρησιμοποιήστε Dispatchers.IO για είσοδο-έξοδο και Dispatchers.Default για υπολογισμούς.
Χρησιμοποιήστε Android Emulator με προφίλ «Slow Network» ή γράψτε μια δοκιμή που καλεί Thread.sleep(6000) στο κύριο νήμα. Εκκινήστε την εφαρμογή μέσω Debug και μετά από 5 δευτερόλεπτα θα δείτε το παράθυρο διαλόγου ANR. Ελέγξτε ότι στο logcat εμφανίστηκε μια εγγραφή ANR με ιχνηλάτηση.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης