onDestroy — finalna metoda cyklu życia Activity i Fragment w Android, wywoływana przed całkowitym zniszczeniem komponentu. onDestroy sygnalizuje, że Activity lub Fragment kończy swoją pracę: wszystkie zasoby muszą zostać zwolnione, zagnieżdżone fragmenty — zniszczone, ViewModel — wyczyszczony. Według danych Google, onDestroy jest wywoływane w 100% przypadków zakończenia Activity, ale przy zabiciu procesu (process death) system może pominąć wywołanie onDestroy całkowicie. Dokumentacja Android na temat onDestroy podkreśla, że ta metoda nie gwarantuje wywołania przy awaryjnym zakończeniu.
Najważniejsze
onDestroy — metoda-wywołanie zwrotne, którą Android wywołuje przed ostatecznym zniszczeniem Activity lub Fragment. To ostatnia szansa dla programisty na zwolnienie zasobów, anulowanie operacji w tle i zakończenie pracy z danymi. Po wykonaniu onDestroy instancja Activity/Fragment jest oznaczana do garbage collectora (GC) i nie może być już użyta.
Przyczyny wywołania onDestroy:
Według statystyk Google Android Vitals (2025), około 12% wszystkich przypadków niszczenia Activity wynika z obrotu ekranu, 65% — z finish() i 23% — ze zmiany konfiguracji. Odsetek zabić procesów z pominięciem onDestroy wynosi około 5–8% w zależności od urządzeń z małą ilością RAM (poniżej 4 GB).
onDestroy jest wywoływane w większości standardowych scenariuszy, ale są ważne wyjątki, które programista musi uwzględnić. Zrozumienie gwarancji wywołania onDestroy jest krytyczne dla architektury aplikacji, zwłaszcza dla zapisywania danych i anulowania zadań WorkManager.
Kiedy onDestroy jest wywoływane:
Kiedy onDestroy NIE jest wywoływane:
Z powodu braku gwarancji wywołania onDestroy Google zaleca: nigdy nie polegaj na onDestroy przy zapisywaniu krytycznych danych. Używaj onSaveInstanceState(), WorkManager lub Room z automatycznym zapisywaniem. onDestroy — dla zwalniania zasobów, ale nie dla trwałości danych.
onDestroy istnieje zarówno dla Activity, jak i dla Fragment, ale z różnymi kontraktami. U Fragment cykl życia jest bardziej szczegółowy: oprócz onDestroy istnieje onDestroyView (zniszczenie hierarchii View) i onDetach (odłączenie od Activity).
| Komponent | Metody niszczenia | Kolejność | ViewModel przetrwa |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | Nie (tylko jeśli ViewModelStore nie został zachowany) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → onStop → onDestroyView → onDestroy → onDetach | Tak, jeśli Fragment nie został usunięty |
Kluczowa różnica: u Fragment View jest odtwarzane częściej niż sam Fragment. Przy obrocie ekranu Fragment przechodzi przez onDestroyView (zniszczenie View), ale sam Fragment i jego ViewModel pozostają żywe. onDestroyView — odpowiednie miejsce do czyszczenia referencji do View, aby uniknąć wycieków pamięci. onDestroy Fragment — odpowiednik onDestroy Activity, wywoływany przy całkowitym usunięciu Fragment.
Zagnieżdżone fragmenty (child fragments) są niszczone przed onDestroy rodzica Fragment. W Activity fragmenty potomne otrzymują onDestroy przy wywołaniu onDestroy rodzica Activity. Kolejność jest gwarantowana: fragmenty kończą się wcześniej niż zawierające je Activity.
onDestroy jest przeznaczone do zwalniania wszystkich zasobów, które nie powinny żyć dłużej niż Activity lub Fragment. W przeciwieństwie do onStop, który zwalnia zasoby do czasu powrotu, onDestroy wykonuje końcowe czyszczenie.
Lista kontrolna obowiązkowych działań w onDestroy:
Czego NIE robić w onDestroy: Nie zapisuj danych w onDestroy — używaj onPause lub onSaveInstanceState. Nie uruchamiaj nowych Service lub WorkManager — Activity zostanie zniszczone i nie będziesz mógł śledzić wyniku. Nie próbuj aktualizować UI — hierarchia View jest już zniszczona lub w trakcie niszczenia; wywołanie findViewById() zwróci null.
ViewModel jest zaprojektowany tak, aby przetrwać onDestroy Activity przy obrocie ekranu, ale być zniszczony razem z Activity przy finish(). To asymetryczne zachowanie — główna przyczyna nieporozumień u programistów.
Przy obrocie ekranu:
Przy finish() (użytkownik nacisnął „Wstecz”):
W związku z tym anulowanie viewModelScope w onDestroy nie jest potrzebne — ViewModel zrobi to samodzielnie. Jeśli używasz lifecycleScope (powiązanego z Activity, a nie z ViewModel), anuluj go w onDestroy przez lifecycleScope.cancel() lub zarządzaj Job ręcznie.
Demonstruje poprawne zarządzanie lifecycleScope w Activity: coroutine jest uruchamiana do śledzenia stanu sieci i anulowana w onDestroy.
class NetworkMonitorActivity : AppCompatActivity() {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d("NetworkMonitor", "Sieć dostępna")
}
override fun onLost(network: Network) {
Log.d("NetworkMonitor", "Sieć utracona")
}
}
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", "Monitorowanie sieci uruchomione")
}
}
override fun onDestroy() {
super.onDestroy()
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.unregisterNetworkCallback(networkCallback)
Log.d("NetworkMonitor", "onDestroy: callback anulowany")
}
}
W onDestroy anulowana jest rejestracja callbacku sieciowego. lifecycleScope jest anulowany automatycznie przy zniszczeniu cyklu życia — oddzielne anulowanie coroutine nie jest wymagane. Callback sieciowy musi zostać wypisany, w przeciwnym razie będzie wisiał w systemie nawet po zniszczeniu Activity.
Fragment poprawnie czyści referencje do View w onDestroyView, zapobiegając wyciekom pamięci spowodowanym przez domknięcia.
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 całkowicie zniszczony")
}
}
W onDestroyView referencje do View są zerowane — zapobiega to wyciekowi pamięci, jeśli domknięcie w imageLoader przechowuje referencję do avatarView. Sam Fragment i jego ViewModel pozostają żywe do onDestroy. imageLoader.cancel() anuluje ładowanie, jeśli Fragment opuszcza ekran.
Użycie isFinishing() pozwala odróżnić, czy Activity kończy się na polecenie użytkownika czy w celu odtworzenia.
class AnalyticsActivity : AppCompatActivity() {
private val analytics = Analytics()
override fun onDestroy() {
if (isFinishing) {
Log.d("AnalyticsActivity", "Activity kończy się finish() — wysyłamy analitykę")
analytics.sendSessionEnd()
} else {
Log.d("AnalyticsActivity", "Activity jest odtwarzane (obrót/konfiguracja) — nie wysyłamy analityki")
}
super.onDestroy()
}
}
Sprawdzanie isFinishing() — ważny wzorzec dla analityki, logowania i czyszczenia danych sesyjnych. Przy obrocie nie trzeba wysyłać zdarzeń zakończenia sesji — użytkownik wciąż pracuje z aplikacją. Według Google Analytics, nieprawidłowe sprawdzanie isFinishing() jest przyczyną 40% fałszywych zdarzeń sesyjnych.
Często zadawane pytania
Tak, może — przy zabiciu procesu przez system (process death), Force Stop przez użytkownika lub awaryjnym zakończeniu. Według danych Google, około 5–8% zakończeń Activity następuje bez wywołania onDestroy. Programista nie powinien polegać na onDestroy przy zapisywaniu krytycznych danych — używaj onPause lub onSaveInstanceState.
finish() — wywołanie inicjujące zniszczenie Activity. onDestroy — callback, który jest wywoływany w trakcie wykonywania finish(). finish() jest wymagane do wywołania onDestroy przy standardowym zakończeniu. finish() może być wywołane przez system lub programistę, onDestroy — tylko systemowy callback.
Tak, koniecznie zarówno w Activity, jak i w Fragment. super.onDestroy() zapewnia poprawne czyszczenie ChildFragmentManager, LoaderManager i innych komponentów systemowych. Pominięcie super.onDestroy() prowadzi do wycieków pamięci i błędów przy przywracaniu fragmentów.
onCleared() jest wywoływane po onDestroy Activity lub Fragment, gdy ViewModel nie jest już potrzebny. Przy obrocie ekranu onCleared() nie jest wywoływane — ViewModel przetrwa onDestroy. Kolejność: onDestroy Activity/Fragment → (ViewModelStore jest czyszczony) → onCleared().
Technicznie tak, ale nie jest zalecane. Activity jest natychmiast niszczone po onDestroy, a uruchomiony Service pozostaje bez kontroli. Do zadań w tle używaj WorkManager z opóźnieniem: WorkManager gwarantuje wykonanie nawet po zakończeniu Activity i przetrwa process death.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również