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 ГБ).
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 обязательно отписывается, иначе он будет висеть в системе даже после уничтожения 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() обязателен для вызова 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также