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 ГБ).

Коли 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.
  • Аварійне завершення — неперехоплений виняток у main-потоці вбиває застосунок без виклику 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, але direct 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 обов'язково відписується, інакше він буде висіти в системі навіть після знищення 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() обов'язковий для виклику 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також