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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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