onDestroy: what it is, finishing Activity work in Android

Author: IT Sectr Published: 2026-03-04 Reading time: 8 min

onDestroy — the final lifecycle method of Activity and Fragment in Android, called before the complete destruction of the component. onDestroy signals that the Activity or Fragment is finishing its work: all resources must be released, nested fragments — destroyed, ViewModel — cleared. According to Google, onDestroy is called in 100% of Activity termination cases, but during process death, the system may skip the onDestroy call entirely. Android Documentation on onDestroy emphasizes that this method does not guarantee invocation during abnormal termination.

Key Takeaways

  • onDestroy — the last call before destroying an Activity or Fragment, intended for final resource cleanup.
  • The onDestroy call is not guaranteed during process death by the system — do not rely on it for saving critical data.
  • In onDestroy, you must cancel background tasks, close sockets and databases, and clear the ViewModelStore.
  • Difference from onStop: onStop — loss of visibility (Activity remains in memory), onDestroy — complete destruction.
  • isFinishing() in onDestroy shows whether the Activity is ending by user command (finish()) or by system decision.

onDestroy: what is it in Android?

onDestroy — a callback method that Android calls before completely destroying an Activity or Fragment. This is the last opportunity for the developer to release resources, cancel background operations, and finalize data work. After onDestroy executes, the Activity/Fragment instance is marked for garbage collection (GC) and can no longer be used.

Reasons for calling onDestroy:

  • Explicit finish() call — the user pressed “Back” or the developer called finishActivity().
  • Screen rotation — the Activity is destroyed and recreated with a new configuration.
  • Configuration change — keyboard, language change, screen size change (multi-window).
  • System decision — Android kills the Activity to free up resources (but onDestroy may not be called).

According to Google Android Vitals statistics (2025), about 12% of all Activity destruction cases occur due to screen rotation, 65% due to finish(), and 23% due to configuration changes. The percentage of process deaths with skipped onDestroy is about 5–8% depending on devices with low RAM (less than 4 GB).

When onDestroy is called — and when it is not called

onDestroy is called in most standard scenarios, but there are important exceptions that the developer must consider. Understanding the guarantees of onDestroy invocation is critical for application architecture, especially for saving data and cancelling WorkManager tasks.

When onDestroy is called:

  • The user presses the “Back” button — Activity.finish() → onPause → onStop → onDestroy.
  • Screen rotation — Activity is destroyed (onPause → onStop → onDestroy), then recreated.
  • Configuration change — system setting requiring Activity recreation.
  • Calling finishAffinity() — finishing all Activities in the stack.
  • Removing a Fragment from FragmentManager — Fragment receives onPause → onStop → onDestroyView → onDestroy → onDetach.

When onDestroy is NOT called:

  • Process death by the system — Android kills the entire application process when memory is low. The Activity does not receive onDestroy because the process terminates at the Linux kernel level.
  • Abnormal termination — an uncaught exception in the main thread kills the application without calling onDestroy.
  • Force Stop — the user forcibly stops the application in settings.

Due to the lack of guarantee for onDestroy invocation, Google recommends: never rely on onDestroy for saving critical data. Use onSaveInstanceState(), WorkManager, or Room with automatic saving. onDestroy is for releasing resources, but not for persistence.

onDestroy in Activity and Fragment: commonalities and differences

onDestroy exists for both Activity and Fragment, but with different contracts. The Fragment lifecycle is more detailed: besides onDestroy, there are onDestroyView (destruction of the View hierarchy) and onDetach (detachment from Activity).

ComponentDestruction methodsOrderViewModel survives
ActivityonDestroyonPause → onStop → onDestroyNo (only if ViewModelStore is not saved)
FragmentonDestroyView, onDestroy, onDetachonPause → onStop → onDestroyView → onDestroy → onDetachYes, if Fragment is not removed

The key difference: a Fragment’s View is recreated more often than the Fragment itself. During screen rotation, the Fragment goes through onDestroyView (View destruction), but the Fragment itself and its ViewModel remain alive. onDestroyView is the right place to clean up View references to avoid memory leaks. Fragment’s onDestroy is analogous to Activity’s onDestroy, called when the Fragment is completely removed.

Child fragments are destroyed before the parent Fragment’s onDestroy. In Activity, child fragments receive onDestroy when the parent Activity’s onDestroy is called. The order is guaranteed: fragments finish before the containing Activity.

What to do in onDestroy: cleanup checklist

onDestroy is intended for releasing all resources that should not outlive the Activity or Fragment. Unlike onStop, which releases resources until returning, onDestroy performs final cleanup.

Checklist of mandatory actions in onDestroy:

  • Cancel coroutines and Flow — cancel jobs that are not bound to viewModelScope. viewModelScope is cancelled automatically, but lifecycleScope is tied to the Activity lifecycle.
  • Close sockets and channels — WebSocket (OkHttp), BluetoothSocket, ServerSocket. Keeping them open after destruction is a system resource leak.
  • Close files and streams — FileInputStream, FileOutputStream, Cursor. A Cursor can cause ANR on ContentProvider if not closed.
  • Unsubscribe from ContentObserver — if the Activity is watching content changes (contacts, media library).
  • Unregister BroadcastReceiver — dynamically registered receivers must be cancelled.
  • Close database — Room closes the connection automatically when the Application is destroyed, but direct SQLiteDatabase requires manual close().

What NOT to do in onDestroy: Do not save data in onDestroy — use onPause or onSaveInstanceState. Do not start new Service or WorkManager tasks — the Activity will be destroyed and you won’t be able to track the result. Do not attempt to update UI — the View hierarchy is already destroyed or in the process of being destroyed; calling findViewById() will return null.

onDestroy and ViewModel: working together

ViewModel is designed to survive onDestroy of Activity during screen rotation, but to be destroyed together with the Activity during finish(). This asymmetric behavior is the main cause of confusion among developers.

During screen rotation:

  • Activity: onPause → onStop → onDestroy (Activity destroyed).
  • ViewModel: NOT destroyed — the ViewModelStore is saved and passed to the new Activity.
  • New Activity: onCreate → onStart → onResume, receives the same ViewModel.

During finish() (user pressed “Back”):

  • Activity: onPause → onStop → onDestroy.
  • ViewModel: onCleared() — called after Activity’s onDestroy.
  • All viewModelScope coroutines are cancelled automatically.

Therefore, cancelling viewModelScope in onDestroy is not necessary — ViewModel will do it on its own. If you are using lifecycleScope (bound to the Activity, not to ViewModel), cancel it in onDestroy via lifecycleScope.cancel() or manage the Job manually.

Code examples with onDestroy in Kotlin

Example 1: onDestroy Activity with lifecycleScope coroutine cancellation

Demonstrates correct lifecycleScope management in an Activity: a coroutine is launched to monitor network status and is cancelled in onDestroy.

kotlin
class NetworkMonitorActivity : AppCompatActivity() {
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            Log.d("NetworkMonitor", "Network available")
        }
        override fun onLost(network: Network) {
            Log.d("NetworkMonitor", "Network lost")
        }
    }

    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", "Network monitoring started")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.unregisterNetworkCallback(networkCallback)
        Log.d("NetworkMonitor", "onDestroy: callback cancelled")
    }
}

In onDestroy, the network callback registration is cancelled. lifecycleScope is cancelled automatically when the lifecycle is destroyed — separate coroutine cancellation is not required. The network callback must be unregistered, otherwise it will remain in the system even after the Activity is destroyed.

Example 2: onDestroy Fragment with View reference cleanup

A Fragment properly clears View references in onDestroyView, preventing memory leaks due to closures.

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 completely destroyed")
    }
}

In onDestroyView, View references are set to null — this prevents memory leaks if a closure in imageLoader holds a reference to avatarView. The Fragment itself and its ViewModel remain alive until onDestroy. imageLoader.cancel() cancels the loading if the Fragment leaves the screen.

Example 3: Checking isFinishing in onDestroy

Using isFinishing() allows distinguishing whether the Activity is ending by user command or for recreation.

kotlin
class AnalyticsActivity : AppCompatActivity() {
    private val analytics = Analytics()

    override fun onDestroy() {
        if (isFinishing) {
            Log.d("AnalyticsActivity", "Activity finishing with finish() — sending analytics")
            analytics.sendSessionEnd()
        } else {
            Log.d("AnalyticsActivity", "Activity being recreated (rotation/configuration) — not sending analytics")
        }
        super.onDestroy()
    }
}

Checking isFinishing() is an important pattern for analytics, logging, and session data cleanup. During rotation, session end events should not be sent — the user is still working with the application. According to Google Analytics, incorrect isFinishing() checking is the cause of 40% of false session events.

Frequently Asked Questions

Can onDestroy not be called?

Yes, it can — during process death by the system, user Force Stop, or abnormal termination. According to Google, about 5–8% of Activity terminations occur without onDestroy being called. Developers should not rely on onDestroy for saving critical data — use onPause or onSaveInstanceState.

What is the difference between onDestroy and finish()?

finish() — a call that initiates Activity destruction. onDestroy — a callback that is called during the execution of finish(). finish() is required for onDestroy to be called during normal termination. finish() can be called by the system or the developer, onDestroy is only a system callback.

Do I need to call super.onDestroy() in Fragment?

Yes, absolutely in both Activity and Fragment. super.onDestroy() ensures proper cleanup of ChildFragmentManager, LoaderManager, and other system components. Skipping super.onDestroy() leads to memory leaks and bugs with fragment restoration.

When is onCleared() called in ViewModel relative to onDestroy?

onCleared() is called after onDestroy of Activity or Fragment, when the ViewModel is no longer needed. During screen rotation, onCleared() is not called — ViewModel survives onDestroy. Order: onDestroy of Activity/Fragment → (ViewModelStore is cleared) → onCleared().

Can I start a Service from onDestroy?

Technically yes, but it is not recommended. The Activity is immediately destroyed after onDestroy, and the started Service remains uncontrolled. For background tasks, use WorkManager with a delay: WorkManager guarantees execution even after the Activity finishes and survives process death.

Summary

  • onDestroy — the final lifecycle callback of Activity and Fragment, called before the complete destruction of the component.
  • The onDestroy call is not guaranteed during process death — approximately 5–8% of terminations occur without it.
  • In onDestroy, you must release: network callbacks, sockets, file streams, BroadcastReceiver, ContentObserver.
  • ViewModel.onCleared() is called after Activity’s onDestroy — viewModelScope is cancelled automatically.
  • onDestroyView in Fragment (separate from onDestroy) — the right place to nullify View references.
  • Checking isFinishing() in onDestroy allows distinguishing finish() termination from recreation due to configuration changes.
  • Do not rely on onDestroy for saving data — use onPause or onSaveInstanceState.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also