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 — 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:
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).
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:
When onDestroy is NOT called:
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 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).
| Component | Destruction methods | Order | ViewModel survives |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | No (only if ViewModelStore is not saved) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → onStop → onDestroyView → onDestroy → onDetach | Yes, 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.
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:
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.
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:
During finish() (user pressed “Back”):
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.
Demonstrates correct lifecycleScope management in an Activity: a coroutine is launched to monitor network status and is cancelled in onDestroy.
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.
A Fragment properly clears View references in onDestroyView, preventing memory leaks due to closures.
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.
Using isFinishing() allows distinguishing whether the Activity is ending by user command or for recreation.
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
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.
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.
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.
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().
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
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.
Read also