onDestroy: шта је, завршетак рада Activity у Android-у

Аутор: IT Sectr Објављено: 2026-03-04 Време читања: 8 мин

onDestroy — финални метод животног циклуса Activity и Fragment у Android-у, који се позива пре потпуног уништавања компоненте. onDestroy сигнализира да Activity или Fragment завршава свој рад: сви ресурси морају бити ослобођени, угњеждени фрагменти — уништени, ViewModel — очишћен. Према Google подацима, onDestroy се позива у 100% случајева завршетка Activity, али при убијању процеса (process death) систем може потпуно да прескочи позив onDestroy. Android документација о onDestroy наглашава да овај метод не гарантује позив при аварном завршетку.

Главно

  • onDestroy — последњи позив пре уништавања Activity или Fragment, намијењен за финално чишћење ресурса.
  • Позив onDestroy није гарантован при убијању процеса од стране система (process death) — не ослањајте се на њега за чување критичних података.
  • У onDestroy је потребно отказати позадинске задатке, затворити сокете и базе података, очистити ViewModelStore.
  • Разлика од onStop: onStop — губитак видљивости (Activity живи у меморији), onDestroy — потпуно уништавање.
  • isFinishing() у onDestroy показује да ли се Activity завршава по команди корисника (finish()) или одлуком система.

onDestroy: шта је ово у Android-у?

onDestroy — метод повратног позива који 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 у стекy.
  • Уклањање 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).

КомпонентаМетоди уништавањаРедослијед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-а.

Угњеждени фрагменти (child fragments) се уништавају прије onDestroy родитељског Fragment-а. У Activity-ју, дјечији фрагменти добијају onDestroy при позиву onDestroy родитељског Activity-ја. Редослијед је гарантован: фрагменти се завршавају раније од Activity које их садржи.

Шта радити у onDestroy: контролна листа чишћења

onDestroy је намијењен за ослобађање свих ресурса који не би требало да живе дуже од Activity или Fragment-а. За разлику од onStop, који ослобађа ресурсе до повратка, onDestroy врши финално чишћење.

Контролна листа обавезних радњи у onDestroy:

  • Отказивање корутина и Flow-а — откажите job-ове који нису везани за viewModelScope. viewModelScope се отказује аутоматски, али lifecycleScope је везан за животни циклус Activity-ја.
  • Затварање сокета и канала — WebSocket (OkHttp), BluetoothSocket, ServerSocket. Држати их отвореним након уништавања је цурење системских ресурса.
  • Затварање датотека и токова — FileInputStream, FileOutputStream, Cursor. Cursor може да изазове ANR на ContentProvider-у ако није затворен.
  • Одјава са ContentObserver-а — ако Activity прати промјене садржаја (контакти, медијатека).
  • Одјава са BroadcastReceiver-а — динамички регистровани receiver-и морају бити отказани.
  • Затварање базе података — 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-ја.
  • Све корутине viewModelScope-а се отказују аутоматски.

Стога, отказивање viewModelScope у onDestroy није потребно — ViewModel ће то урадити сам. Ако користите lifecycleScope (везан за Activity, не за ViewModel), откажите га у onDestroy путем lifecycleScope.cancel() или управљајте Job-ом ручно.

Примјери кода са onDestroy у Kotlin-у

Примјер 1: onDestroy Activity са отказивањем корутине lifecycleScope

Демонстрира правилно управљање lifecycleScope у Activity-ју: корутина се покреће за праћење мрежног статуса и отказује се у 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 се отказује регистрација мрежног повратног позива. lifecycleScope се отказује аутоматски при уништавању животног циклуса — одвојено отказивање корутине није потребно. Мрежни callback se мора одјавити, иначе ће висити у систему чак и након уништавања Activity-ја.

Примјер 2: onDestroy Fragment са чишћењем View референци

Fragment правилно чисти референце на View у onDestroyView, спречавајући цурење меморије узроковано затварањима.

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 се постављају на нулу — ово спречава цурење меморије ако затварање у 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 — повратни позив који се позива у процесу извршења finish(). finish() je обавезан за позив onDestroy при нормалном завршетку. finish() може позвати систем или програмер, onDestroy — само системски повратни позив.

Да ли треба позвати super.onDestroy() у Fragment-у?

Да, обавезно како у Activity, тако и у Fragment-у. super.onDestroy() обезбјеђује правилно чишћење ChildFragmentManager, LoaderManager и других системских компоненти. Пропуштање super.onDestroy() доводи до цурења меморије и грешака при обнављању фрагмената.

Када се 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 — финални повратни позив животног циклуса Activity и Fragment, који се позива прије потпуног уништавања компоненте.
  • Позив onDestroy није гарантован при process death — око 5–8% завршетака се дешава без њега.
  • У onDestroy треба ослободити: мрежне повратне позиве, сокете, токове датотека, BroadcastReceiver, ContentObserver.
  • ViewModel.onCleared() се позива након onDestroy Activity — viewModelScope се отказује аутоматски.
  • onDestroyView код Fragment-а (одвојено од onDestroy) — правилно мјесто за нулирање референци на View.
  • Провјера isFinishing() у onDestroy омогућава разликовање завршетка finish() од поновног стварања при конфигурацијским промјенама.
  • Не ослањајте се на onDestroy за чување података — користите onPause или onSaveInstanceState.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође