onDestroy: τι είναι, τερματισμός Activity στο Android

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

onDestroy — η τελική μέθοδος του κύκλου ζωής Activity και Fragment στο Android, που καλείται πριν από την πλήρη καταστροφή του component. Το onDestroy σηματοδοτεί ότι το Activity ή Fragment ολοκληρώνει τη λειτουργία του: όλοι οι πόροι πρέπει να απελευθερωθούν, τα ένθετα fragments — να καταστραφούν, το ViewModel — να εκκαθαριστεί. Σύμφωνα με δεδομένα της Google, το onDestroy καλείται στο 100% των περιπτώσεων τερματισμού Activity, αλλά κατά τη θανάτωση διεργασίας (process death) το σύστημα μπορεί να παραλείψει εντελώς την κλήση του onDestroy. Η τεκμηρίωση Android για το onDestroy τονίζει ότι αυτή η μέθοδος δεν εγγυάται κλήση σε περίπτωση έκτακτου τερματισμού.

Κύρια Σημεία

  • onDestroy — η τελευταία κλήση πριν από την καταστροφή Activity ή Fragment, που προορίζεται για την τελική εκκαθάριση πόρων.
  • Η κλήση του onDestroy δεν είναι εγγυημένη κατά τη θανάτωση διεργασίας από το σύστημα (process death) — μην βασίζεστε σε αυτήν για αποθήκευση κρίσιμων δεδομένων.
  • Στο onDestroy πρέπει να ακυρώνονται οι εργασίες παρασκηνίου, να κλείνουν sockets και βάσεις δεδομένων, να εκκαθαρίζεται το ViewModelStore.
  • Διαφορά από το onStop: onStop — απώλεια ορατότητας (Activity ζει στη μνήμη), onDestroy — πλήρης καταστροφή.
  • Το isFinishing() στο onDestroy δείχνει αν το Activity τερματίζεται με εντολή χρήστη (finish()) ή με απόφαση συστήματος.

onDestroy: τι είναι στο Android;

onDestroy — μια μέθοδος callback που καλεί το Android πριν καταστρέψει οριστικά το Activity ή Fragment. Αυτή είναι η τελευταία ευκαιρία για τον προγραμματιστή να απελευθερώσει πόρους, να ακυρώσει λειτουργίες παρασκηνίου και να ολοκληρώσει την εργασία με δεδομένα. Μετά την εκτέλεση του onDestroy, η παρουσία Activity/Fragment επισημαίνεται για συλλογή σκουπιδιών (GC) και δεν μπορεί πλέον να χρησιμοποιηθεί.

Αιτίες κλήσης του onDestroy:

  • Ρητή κλήση finish() — ο χρήστης πάτησε "Πίσω" ή ο προγραμματιστής κάλεσε το finishActivity().
  • Περιστροφή οθόνης — το Activity καταστρέφεται και δημιουργείται ξανά με νέα διαμόρφωση.
  • Αλλαγή διαμόρφωσης — πληκτρολόγιο, αλλαγή γλώσσας, αλλαγή μεγέθους οθόνης (multi-window).
  • Απόφαση συστήματος — το Android σκοτώνει το Activity για απελευθέρωση πόρων (αλλά το onDestroy μπορεί να μην κληθεί).

Σύμφωνα με στατιστικά Google Android Vitals (2025), περίπου 12% όλων των περιπτώσεων καταστροφής Activity οφείλονται σε περιστροφή οθόνης, 65% σε finish() και 23% σε αλλαγή διαμόρφωσης. Το ποσοστό θανάτωσης διεργασιών με παράλειψη του onDestroy είναι περίπου 5–8% ανάλογα με συσκευές με μικρή RAM (λιγότερη από 4 GB).

Πότε καλείται το onDestroy — και πότε όχι

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

Πότε καλείται το onDestroy:

  • Ο χρήστης πατά το κουμπί "Πίσω" — Activity.finish() → onPause → onStop → onDestroy.
  • Περιστροφή οθόνης — το Activity καταστρέφεται (onPause → onStop → onDestroy), στη συνέχεια δημιουργείται ξανά.
  • Αλλαγή διαμόρφωσης — ρύθμιση συστήματος που απαιτεί αναδημιουργία του Activity.
  • Κλήση finishAffinity() — τερματισμός όλων των Activity στη στοίβα.
  • Αφαίρεση Fragment από το FragmentManager — το Fragment λαμβάνει onPause → onStop → onDestroyView → onDestroy → onDetach.

Πότε ΔΕΝ καλείται το onDestroy:

  • Θανάτωση διεργασίας από το σύστημα (process death) — το Android σκοτώνει ολόκληρη τη διεργασία της εφαρμογής σε έλλειψη μνήμης. Το Activity δεν λαμβάνει onDestroy, καθώς η διεργασία τερματίζεται σε επίπεδο πυρήνα Linux.
  • Έκτακτος τερματισμός — μια μη πιασμένη εξαίρεση στο κύριο νήμα σκοτώνει την εφαρμογή χωρίς κλήση onDestroy.
  • Force Stop — ο χρήστης σταματά αναγκαστικά την εφαρμογή στις ρυθμίσεις.

Λόγω έλλειψης εγγύησης κλήσης του onDestroy, η Google συνιστά: μην βασίζεστε ποτέ στο onDestroy για αποθήκευση κρίσιμων δεδομένων. Χρησιμοποιήστε onSaveInstanceState(), WorkManager ή Room με αυτόματη αποθήκευση. Το onDestroy — για απελευθέρωση πόρων, όχι για μονιμότητα δεδομένων.

onDestroy σε Activity και Fragment: κοινά και διαφορές

Το onDestroy υπάρχει τόσο για Activity όσο και για Fragment, αλλά με διαφορετικές συμβάσεις. Στο Fragment, ο κύκλος ζωής είναι πιο λεπτομερής: εκτός από onDestroy υπάρχουν onDestroyView (καταστροφή ιεραρχίας View) και onDetach (αποσύνδεση από το Activity).

ComponentΜέθοδοι καταστροφήςΣειράViewModel επιβιώνει
ActivityonDestroyonPause → onStop → onDestroyΌχι (μόνο αν το ViewModelStore δεν αποθηκεύτηκε)
FragmentonDestroyView, onDestroy, onDetachonPause → onStop → onDestroyView → onDestroy → onDetachΝαι, αν το Fragment δεν αφαιρέθηκε

Βασική διαφορά: στο Fragment, το View αναδημιουργείται συχνότερα από το ίδιο το Fragment. Κατά την περιστροφή οθόνης, το Fragment περνά από onDestroyView (καταστροφή View), αλλά το ίδιο το Fragment και το ViewModel του παραμένουν ζωντανά. onDestroyView — το σωστό μέρος για εκκαθάριση αναφορών σε View για αποφυγή διαρροών μνήμης. Το onDestroy του Fragment — ανάλογο του onDestroy του Activity, καλείται κατά την πλήρη αφαίρεση του Fragment.

Τα ένθετα fragments (child fragments) καταστρέφονται πριν από το onDestroy του γονικού Fragment. Στο Activity, τα θυγατρικά fragments λαμβάνουν onDestroy όταν καλείται το onDestroy του γονικού Activity. Η σειρά είναι εγγυημένη: τα fragments ολοκληρώνονται νωρίτερα από το Activity που τα περιέχει.

Τι να κάνετε στο onDestroy: λίστα ελέγχου εκκαθάρισης

Το onDestroy προορίζεται για απελευθέρωση όλων των πόρων που δεν πρέπει να ζουν περισσότερο από το Activity ή Fragment. Σε αντίθεση με το onStop, που απελευθερώνει πόρους μέχρι την επιστροφή, το onDestroy εκτελεί τελική εκκαθάριση.

Λίστα ελέγχου υποχρεωτικών ενεργειών στο onDestroy:

  • Ακύρωση coroutine και Flow — ακυρώστε jobs που δεν είναι συνδεδεμένα με το viewModelScope. Το viewModelScope ακυρώνεται αυτόματα, αλλά το lifecycleScope είναι συνδεδεμένο με τον κύκλο ζωής του Activity.
  • Κλείσιμο sockets και καναλιών — WebSocket (OkHttp), BluetoothSocket, ServerSocket. Το να τα κρατάτε ανοιχτά μετά την καταστροφή είναι διαρροή πόρων συστήματος.
  • Κλείσιμο αρχείων και ροών — FileInputStream, FileOutputStream, Cursor. Το Cursor μπορεί να προκαλέσει ANR στο ContentProvider αν δεν κλείσει.
  • Αποεγγραφή από ContentObserver — αν το Activity παρακολουθεί αλλαγές περιεχομένου (επαφές, βιβλιοθήκη πολυμέσων).
  • Αποεγγραφή από BroadcastReceiver — οι δυναμικά καταχωρημένοι receivers πρέπει να ακυρωθούν.
  • Κλείσιμο βάσης δεδομένων — το Room κλείνει αυτόματα τη σύνδεση κατά την καταστροφή του Application, αλλά το άμεσο SQLiteDatabase απαιτεί χειροκίνητο close().

Τι ΝΑ ΜΗΝ κάνετε στο onDestroy: Μην αποθηκεύετε δεδομένα στο onDestroy — χρησιμοποιήστε onPause ή onSaveInstanceState. Μην ξεκινάτε νέο Service ή WorkManager — το Activity θα καταστραφεί και δεν θα μπορείτε να παρακολουθήσετε το αποτέλεσμα. Μην επιχειρείτε να ενημερώσετε το UI — η ιεραρχία View έχει ήδη καταστραφεί ή είναι υπό καταστροφή; η κλήση findViewById() θα επιστρέψει null.

onDestroy και ViewModel: συνεργασία

Το ViewModel είναι σχεδιασμένο να επιβιώνει του onDestroy Activity κατά την περιστροφή οθόνης, αλλά να καταστρέφεται μαζί με το Activity στο finish(). Αυτή η ασύμμετρη συμπεριφορά — η κύρια αιτία σύγχυσης μεταξύ προγραμματιστών.

Κατά την περιστροφή οθόνης:

  • Activity: onPause → onStop → onDestroy (Activity καταστράφηκε).
  • ViewModel: ΔΕΝ καταστράφηκε — το ViewModelStore αποθηκεύεται και μεταβιβάζεται στο νέο Activity.
  • Νέο Activity: onCreate → onStart → onResume, λαμβάνει το ίδιο ViewModel.

Στο finish() (ο χρήστης πάτησε "Πίσω"):

  • Activity: onPause → onStop → onDestroy.
  • ViewModel: onCleared() — καλείται μετά το onDestroy του Activity.
  • Όλα τα coroutine του viewModelScope ακυρώνονται αυτόματα.

Επομένως, η ακύρωση του viewModelScope στο onDestroy δεν είναι απαραίτητη — το ViewModel θα το κάνει μόνο του. Αν χρησιμοποιείτε lifecycleScope (συνδεδεμένο με το Activity, όχι με το ViewModel), ακυρώστε το στο onDestroy μέσω lifecycleScope.cancel() ή διαχειριστείτε το Job χειροκίνητα.

Παραδείγματα κώδικα με onDestroy σε Kotlin

Παράδειγμα 1: onDestroy Activity με ακύρωση coroutine lifecycleScope

Δείχνει σωστή διαχείριση του lifecycleScope στο Activity: το coroutine ξεκινά για παρακολούθηση κατάστασης δικτύου και ακυρώνεται στο onDestroy.

kotlin
class NetworkMonitorActivity : AppCompatActivity() {
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            Log.d("NetworkMonitor", "Δίκτυο διαθέσιμο")
        }
        override fun onLost(network: Network) {
            Log.d("NetworkMonitor", "Δίκτυο χάθηκε")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_network)
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.registerDefaultNetworkCallback(networkCallback)
        lifecycleScope.launch {
            Log.d("NetworkMonitor", "Παρακολούθηση δικτύου ξεκίνησε")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.unregisterNetworkCallback(networkCallback)
        Log.d("NetworkMonitor", "onDestroy: callback ακυρώθηκε")
    }
}

Στο onDestroy ακυρώνεται η καταχώρηση του callback δικτύου. Το lifecycleScope ακυρώνεται αυτόματα κατά την καταστροφή του κύκλου ζωής — ξεχωριστή ακύρωση του coroutine δεν απαιτείται. Το callback δικτύου πρέπει υποχρεωτικά να αποεγγραφεί, διαφορετικά θα παραμείνει στο σύστημα ακόμα και μετά την καταστροφή του Activity.

Παράδειγμα 2: onDestroy Fragment με εκκαθάριση αναφορών View

Το Fragment εκκαθαρίζει σωστά τις αναφορές στο View στο onDestroyView, αποτρέποντας διαρροές μνήμης που προκαλούνται από closures.

kotlin
class ProfileFragment : Fragment() {
    private var avatarView: ImageView? = null
    private var progressBar: ProgressBar? = null
    private val imageLoader = ImageLoader()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        avatarView = view.findViewById(R.id.avatar)
        progressBar = view.findViewById(R.id.progress)
        loadProfile()
    }

    private fun loadProfile() {
        viewLifecycleOwner.lifecycleScope.launch {
            try {
                progressBar?.visibility = View.VISIBLE
                val bitmap = imageLoader.load("https://example.com/avatar.png")
                avatarView?.setImageBitmap(bitmap)
            } finally {
                progressBar?.visibility = View.GONE
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        avatarView = null
        progressBar = null
        imageLoader.cancel()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("ProfileFragment", "onDestroy: Fragment καταστράφηκε πλήρως")
    }
}

Στο onDestroyView οι αναφορές στο View μηδενίζονται — αυτό αποτρέπει διαρροή μνήμης αν το closure στο imageLoader κρατά αναφορά στο avatarView. Το ίδιο το Fragment και το ViewModel του παραμένουν ζωντανά μέχρι το onDestroy. Το imageLoader.cancel() ακυρώνει τη φόρτωση αν το Fragment φύγει από την οθόνη.

Παράδειγμα 3: Έλεγχος isFinishing στο onDestroy

Η χρήση του isFinishing() επιτρέπει τη διάκριση αν το Activity τερματίζεται με εντολή χρήστη ή για αναδημιουργία.

kotlin
class AnalyticsActivity : AppCompatActivity() {
    private val analytics = Analytics()

    override fun onDestroy() {
        if (isFinishing) {
            Log.d("AnalyticsActivity", "Activity τερματίζεται με finish() — στέλνουμε αναλυτικά")
            analytics.sendSessionEnd()
        } else {
            Log.d("AnalyticsActivity", "Activity αναδημιουργείται (περιστροφή/διαμόρφωση) — δεν στέλνουμε αναλυτικά")
        }
        super.onDestroy()
    }
}

Ο έλεγχος isFinishing() — σημαντικό μοτίβο για αναλυτικά, καταγραφή και εκκαθάριση δεδομένων συνεδρίας. Στην περιστροφή δεν χρειάζεται να αποστέλλονται συμβάντα λήξης συνεδρίας — ο χρήστης εξακολουθεί να εργάζεται με την εφαρμογή. Σύμφωνα με το Google Analytics, ο λανθασμένος έλεγχος isFinishing() είναι η αιτία του 40% των ψευδών συμβάντων συνεδρίας.

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

Μπορεί το onDestroy να μην κληθεί;

Ναι, μπορεί — κατά τη θανάτωση διεργασίας από το σύστημα (process death), Force Stop από τον χρήστη ή έκτακτο τερματισμό. Σύμφωνα με δεδομένα της Google, περίπου 5–8% των τερματισμών Activity συμβαίνουν χωρίς κλήση onDestroy. Ο προγραμματιστής δεν πρέπει να βασίζεται στο onDestroy για αποθήκευση κρίσιμων δεδομένων — χρησιμοποιήστε onPause ή onSaveInstanceState.

Σε τι διαφέρει το onDestroy από το finish();

finish() — η κλήση που ξεκινά την καταστροφή του Activity. onDestroy — το callback που καλείται κατά τη διαδικασία εκτέλεσης του finish(). Το finish() είναι υποχρεωτικό για την κλήση του onDestroy σε κανονικό τερματισμό. Το finish() μπορεί να κληθεί από το σύστημα ή τον προγραμματιστή, το onDestroy — μόνο callback συστήματος.

Πρέπει να καλείται το super.onDestroy() στο Fragment;

Ναι, υποχρεωτικά τόσο στο Activity όσο και στο Fragment. Το super.onDestroy() εξασφαλίζει σωστή εκκαθάριση του ChildFragmentManager, LoaderManager και άλλων component του συστήματος. Η παράλειψη του super.onDestroy() οδηγεί σε διαρροές μνήμης και σφάλματα κατά την επαναφορά fragment.

Πότε καλείται το onCleared() στο ViewModel σε σχέση με το onDestroy;

Το onCleared() καλείται μετά το onDestroy του Activity ή Fragment, όταν το ViewModel δεν χρειάζεται πλέον. Κατά την περιστροφή οθόνης το onCleared() δεν καλείται — το ViewModel επιβιώνει του onDestroy. Σειρά: onDestroy Activity/Fragment → (ViewModelStore εκκαθαρίζεται) → onCleared().

Μπορεί να ξεκινήσει Service από το onDestroy;

Τεχνικά ναι, αλλά δεν συνιστάται. Το Activity καταστρέφεται αμέσως μετά το onDestroy και το Service που ξεκίνησε παραμένει χωρίς έλεγχο. Για εργασίες παρασκηνίου χρησιμοποιήστε WorkManager με καθυστέρηση: το WorkManager εγγυάται εκτέλεση ακόμα και μετά τον τερματισμό του Activity και επιβιώνει του process death.

Σύνοψη

  • onDestroy — το τελικό callback του κύκλου ζωής Activity και Fragment, που καλείται πριν από την πλήρη καταστροφή του component.
  • Η κλήση του onDestroy δεν είναι εγγυημένη σε process death — περίπου 5–8% τερματισμοί συμβαίνουν χωρίς αυτό.
  • Στο onDestroy πρέπει να απελευθερώνονται: callbacks δικτύου, sockets, ροές αρχείων, BroadcastReceiver, ContentObserver.
  • Το ViewModel.onCleared() καλείται μετά το onDestroy του Activity — το viewModelScope ακυρώνεται αυτόματα.
  • Το onDestroyView στο Fragment (ξεχωριστά από το onDestroy) — το σωστό μέρος για μηδενισμό αναφορών στο View.
  • Ο έλεγχος isFinishing() στο onDestroy επιτρέπει τη διάκριση τερματισμού finish() από αναδημιουργία σε αλλαγές διαμόρφωσης.
  • Μην βασίζεστε στο onDestroy για αποθήκευση δεδομένων — χρησιμοποιήστε onPause ή onSaveInstanceState.

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

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

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

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