onDestroy — η τελική μέθοδος του κύκλου ζωής Activity και Fragment στο Android, που καλείται πριν από την πλήρη καταστροφή του component. Το onDestroy σηματοδοτεί ότι το Activity ή Fragment ολοκληρώνει τη λειτουργία του: όλοι οι πόροι πρέπει να απελευθερωθούν, τα ένθετα fragments — να καταστραφούν, το ViewModel — να εκκαθαριστεί. Σύμφωνα με δεδομένα της Google, το onDestroy καλείται στο 100% των περιπτώσεων τερματισμού Activity, αλλά κατά τη θανάτωση διεργασίας (process death) το σύστημα μπορεί να παραλείψει εντελώς την κλήση του onDestroy. Η τεκμηρίωση Android για το onDestroy τονίζει ότι αυτή η μέθοδος δεν εγγυάται κλήση σε περίπτωση έκτακτου τερματισμού.
Κύρια Σημεία
onDestroy — μια μέθοδος callback που καλεί το Android πριν καταστρέψει οριστικά το Activity ή Fragment. Αυτή είναι η τελευταία ευκαιρία για τον προγραμματιστή να απελευθερώσει πόρους, να ακυρώσει λειτουργίες παρασκηνίου και να ολοκληρώσει την εργασία με δεδομένα. Μετά την εκτέλεση του onDestroy, η παρουσία Activity/Fragment επισημαίνεται για συλλογή σκουπιδιών (GC) και δεν μπορεί πλέον να χρησιμοποιηθεί.
Αιτίες κλήσης του onDestroy:
Σύμφωνα με στατιστικά Google Android Vitals (2025), περίπου 12% όλων των περιπτώσεων καταστροφής Activity οφείλονται σε περιστροφή οθόνης, 65% σε finish() και 23% σε αλλαγή διαμόρφωσης. Το ποσοστό θανάτωσης διεργασιών με παράλειψη του onDestroy είναι περίπου 5–8% ανάλογα με συσκευές με μικρή RAM (λιγότερη από 4 GB).
Το onDestroy καλείται στα περισσότερα τυπικά σενάρια, αλλά υπάρχουν σημαντικές εξαιρέσεις που πρέπει να λάβει υπόψη του ο προγραμματιστής. Η κατανόηση των εγγυήσεων κλήσης του onDestroy είναι κρίσιμη για την αρχιτεκτονική της εφαρμογής, ειδικά για την αποθήκευση δεδομένων και την ακύρωση εργασιών WorkManager.
Πότε καλείται το onDestroy:
Πότε ΔΕΝ καλείται το onDestroy:
Λόγω έλλειψης εγγύησης κλήσης του onDestroy, η Google συνιστά: μην βασίζεστε ποτέ στο onDestroy για αποθήκευση κρίσιμων δεδομένων. Χρησιμοποιήστε onSaveInstanceState(), WorkManager ή Room με αυτόματη αποθήκευση. Το onDestroy — για απελευθέρωση πόρων, όχι για μονιμότητα δεδομένων.
Το onDestroy υπάρχει τόσο για Activity όσο και για Fragment, αλλά με διαφορετικές συμβάσεις. Στο Fragment, ο κύκλος ζωής είναι πιο λεπτομερής: εκτός από onDestroy υπάρχουν onDestroyView (καταστροφή ιεραρχίας View) και onDetach (αποσύνδεση από το Activity).
| Component | Μέθοδοι καταστροφής | Σειρά | ViewModel επιβιώνει |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | Όχι (μόνο αν το ViewModelStore δεν αποθηκεύτηκε) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → 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 προορίζεται για απελευθέρωση όλων των πόρων που δεν πρέπει να ζουν περισσότερο από το Activity ή Fragment. Σε αντίθεση με το onStop, που απελευθερώνει πόρους μέχρι την επιστροφή, το onDestroy εκτελεί τελική εκκαθάριση.
Λίστα ελέγχου υποχρεωτικών ενεργειών στο onDestroy:
Τι ΝΑ ΜΗΝ κάνετε στο onDestroy: Μην αποθηκεύετε δεδομένα στο onDestroy — χρησιμοποιήστε onPause ή onSaveInstanceState. Μην ξεκινάτε νέο Service ή WorkManager — το Activity θα καταστραφεί και δεν θα μπορείτε να παρακολουθήσετε το αποτέλεσμα. Μην επιχειρείτε να ενημερώσετε το UI — η ιεραρχία View έχει ήδη καταστραφεί ή είναι υπό καταστροφή; η κλήση findViewById() θα επιστρέψει null.
Το ViewModel είναι σχεδιασμένο να επιβιώνει του onDestroy Activity κατά την περιστροφή οθόνης, αλλά να καταστρέφεται μαζί με το Activity στο finish(). Αυτή η ασύμμετρη συμπεριφορά — η κύρια αιτία σύγχυσης μεταξύ προγραμματιστών.
Κατά την περιστροφή οθόνης:
Στο finish() (ο χρήστης πάτησε "Πίσω"):
Επομένως, η ακύρωση του viewModelScope στο onDestroy δεν είναι απαραίτητη — το ViewModel θα το κάνει μόνο του. Αν χρησιμοποιείτε lifecycleScope (συνδεδεμένο με το Activity, όχι με το ViewModel), ακυρώστε το στο onDestroy μέσω lifecycleScope.cancel() ή διαχειριστείτε το Job χειροκίνητα.
Δείχνει σωστή διαχείριση του lifecycleScope στο Activity: το coroutine ξεκινά για παρακολούθηση κατάστασης δικτύου και ακυρώνεται στο onDestroy.
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.
Το Fragment εκκαθαρίζει σωστά τις αναφορές στο View στο onDestroyView, αποτρέποντας διαρροές μνήμης που προκαλούνται από closures.
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 φύγει από την οθόνη.
Η χρήση του isFinishing() επιτρέπει τη διάκριση αν το Activity τερματίζεται με εντολή χρήστη ή για αναδημιουργία.
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% των ψευδών συμβάντων συνεδρίας.
Συχνές Ερωτήσεις
Ναι, μπορεί — κατά τη θανάτωση διεργασίας από το σύστημα (process death), Force Stop από τον χρήστη ή έκτακτο τερματισμό. Σύμφωνα με δεδομένα της Google, περίπου 5–8% των τερματισμών Activity συμβαίνουν χωρίς κλήση onDestroy. Ο προγραμματιστής δεν πρέπει να βασίζεται στο onDestroy για αποθήκευση κρίσιμων δεδομένων — χρησιμοποιήστε onPause ή onSaveInstanceState.
finish() — η κλήση που ξεκινά την καταστροφή του Activity. onDestroy — το callback που καλείται κατά τη διαδικασία εκτέλεσης του finish(). Το finish() είναι υποχρεωτικό για την κλήση του onDestroy σε κανονικό τερματισμό. Το finish() μπορεί να κληθεί από το σύστημα ή τον προγραμματιστή, το onDestroy — μόνο callback συστήματος.
Ναι, υποχρεωτικά τόσο στο Activity όσο και στο Fragment. Το super.onDestroy() εξασφαλίζει σωστή εκκαθάριση του ChildFragmentManager, LoaderManager και άλλων component του συστήματος. Η παράλειψη του super.onDestroy() οδηγεί σε διαρροές μνήμης και σφάλματα κατά την επαναφορά fragment.
Το onCleared() καλείται μετά το onDestroy του Activity ή Fragment, όταν το ViewModel δεν χρειάζεται πλέον. Κατά την περιστροφή οθόνης το onCleared() δεν καλείται — το ViewModel επιβιώνει του onDestroy. Σειρά: onDestroy Activity/Fragment → (ViewModelStore εκκαθαρίζεται) → onCleared().
Τεχνικά ναι, αλλά δεν συνιστάται. Το Activity καταστρέφεται αμέσως μετά το onDestroy και το Service που ξεκίνησε παραμένει χωρίς έλεγχο. Για εργασίες παρασκηνίου χρησιμοποιήστε WorkManager με καθυστέρηση: το WorkManager εγγυάται εκτέλεση ακόμα και μετά τον τερματισμό του Activity και επιβιώνει του process death.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης