onDestroy — финални метод животног циклуса Activity и Fragment у Android-у, који се позива пре потпуног уништавања компоненте. onDestroy сигнализира да Activity или Fragment завршава свој рад: сви ресурси морају бити ослобођени, угњеждени фрагменти — уништени, ViewModel — очишћен. Према Google подацима, onDestroy се позива у 100% случајева завршетка Activity, али при убијању процеса (process death) систем може потпуно да прескочи позив onDestroy. Android документација о onDestroy наглашава да овај метод не гарантује позив при аварном завршетку.
Главно
onDestroy — метод повратног позива који 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).
| Компонента | Методи уништавања | Редослијед | 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-а.
Угњеждени фрагменти (child fragments) се уништавају прије onDestroy родитељског Fragment-а. У Activity-ју, дјечији фрагменти добијају onDestroy при позиву onDestroy родитељског Activity-ја. Редослијед је гарантован: фрагменти се завршавају раније од 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-ју: корутина се покреће за праћење мрежног статуса и отказује се у 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 се отказује регистрација мрежног повратног позива. lifecycleScope се отказује аутоматски при уништавању животног циклуса — одвојено отказивање корутине није потребно. Мрежни callback se мора одјавити, иначе ће висити у систему чак и након уништавања Activity-ја.
Fragment правилно чисти референце на View у onDestroyView, спречавајући цурење меморије узроковано затварањима.
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 се постављају на нулу — ово спречава цурење меморије ако затварање у 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 — повратни позив који се позива у процесу извршења finish(). finish() je обавезан за позив onDestroy при нормалном завршетку. finish() може позвати систем или програмер, onDestroy — само системски повратни позив.
Да, обавезно како у Activity, тако и у Fragment-у. super.onDestroy() обезбјеђује правилно чишћење ChildFragmentManager, LoaderManager и других системских компоненти. Пропуштање super.onDestroy() доводи до цурења меморије и грешака при обнављању фрагмената.
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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође