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 в стека.
  • Премахване на 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 трябва задължително да се отпише, иначе ще виси в системата дори след унищожаване на 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() e задължително за извикване на 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 трябва да се освободят: мрежови callback-ове, сокети, файлови потоци, BroadcastReceiver, ContentObserver.
  • ViewModel.onCleared() се извиква след onDestroy на Activity — viewModelScope се отменя автоматично.
  • onDestroyView във Fragment (отделно от onDestroy) — правилното място за зануляване на референции към View.
  • Проверката на isFinishing() в onDestroy позволява да се различи завършване чрез finish() от пресъздаване при конфигурационни промени.
  • Не разчитайте на onDestroy за запазване на данни — използвайте onPause или onSaveInstanceState.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също