Table of Contents
Understanding Lifecycle Callbacks
Lifecycle callbacks are specific methods that Android calls on your Activity at different moments of its life. They are your main hooks to run code when the activity is created, becomes visible, gets focus, goes to the background, or is destroyed.
In this chapter you will focus on what each callback means in practice, how they are ordered, and what kinds of work are appropriate in each one. You will not re-learn the basic lifecycle overview, but instead look closely at the callbacks themselves.
Lifecycle callbacks are called by the system, not by you. You override them in your Activity or Fragment, but you almost never call them directly.
The Core Activity Lifecycle Methods
The core lifecycle callbacks of an Activity are:
onCreate, onStart, onResume, onPause, onStop, onDestroy, and onRestart.
You already know the general lifecycle sequence, so here the focus is on what to put where, and how to implement them correctly.
A minimal Activity with all callbacks looks like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set your layout and basic setup here
}
override fun onStart() {
super.onStart()
// Activity is becoming visible
}
override fun onResume() {
super.onResume()
// Activity is now in the foreground and interactive
}
override fun onPause() {
super.onPause()
// Activity is losing focus. Pause ongoing work that should not continue.
}
override fun onStop() {
super.onStop()
// Activity is no longer visible. Release or adjust resources.
}
override fun onRestart() {
super.onRestart()
// Activity is starting again after being stopped
}
override fun onDestroy() {
super.onDestroy()
// Final cleanup before activity is destroyed
}
}
Each overridden method must call its superclass implementation using super.methodName(...). This lets the base Activity class do its internal work before or after your custom code.
Always call super in lifecycle callbacks unless official documentation explicitly says otherwise. Skipping super can cause subtle bugs and inconsistent behavior.
onCreate: One-Time Initialization
onCreate is called when the activity is first created. It is usually called once for the lifetime of the activity instance.
In onCreate you typically:
Set the layout with setContentView.
Find views with findViewById or view binding.
Restore simple saved state from the savedInstanceState Bundle.
Set up adapters, listeners, and data structures that you will reuse while the activity lives.
Example:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.myButton)
button.setOnClickListener {
// Handle button click
}
if (savedInstanceState != null) {
val counter = savedInstanceState.getInt("counter", 0)
// Restore your counter here
}
}
onCreate is not the right place to start animations or play media that should only run when the user can actually see and interact with the UI. Those belong in later callbacks.
onStart: Becoming Visible
onStart is called right after onCreate and also when the activity becomes visible again after being stopped.
The activity is visible at this point, but the user might not yet be able to interact with it, for example when a transition animation is still running.
You can use onStart to:
Register any listeners or receivers that are needed only while the activity is visible.
Perform lightweight initialization that you might need every time the activity comes back to the foreground, not just the very first time.
Example:
override fun onStart() {
super.onStart()
// For example, start listening to a data source that updates the UI
}onResume: Ready for Interaction
onResume is called when the activity starts interacting with the user. The activity is in the foreground and has input focus.
Typical work in onResume includes:
Starting animations that should only run when the activity is visible and focused.
Starting camera preview, sensor updates, or starting to listen for location if those are directly tied to user interaction.
Resuming a paused game, music playback UI, or timers that should only run while the activity is active.
Example:
override fun onResume() {
super.onResume()
// Resume animations, start camera preview, restart paused tasks
}
onResume is called every time the activity enters the foreground, not only after onCreate, so avoid initializing things here that should be created once. Prefer to create long-lived objects in onCreate or onStart and only resume them in onResume.
onPause: Losing Focus
onPause is the first indication that the user is leaving your activity. It can be followed by onStop if the activity becomes invisible, or followed by onResume again if the user quickly goes back.
You should keep onPause fast, because the system wants to move to the next activity quickly.
Tasks for onPause:
Pause ongoing work that must not continue while the activity does not have focus, such as a game loop, camera preview, or some continuous animation.
Commit unsaved user changes that must not be lost if the app is killed while in the background, such as writing text input to SharedPreferences.
Stop expensive operations that do not make sense to run if the user is not interacting.
Example:
override fun onPause() {
super.onPause()
// Pause game, stop ongoing animation, save draft text
}
Avoid heavy resource cleanup here if it is not urgent, because that may slow down the transition to the next activity. Save deeper cleanup for onStop or onDestroy.
onStop: No Longer Visible
onStop is called when your activity is no longer visible to the user. This may happen because a new activity covered it completely, or the app went to the background.
In onStop you typically:
Release resources that are not needed while the activity is invisible, such as camera, sensors, BroadcastReceivers registered in onStart, or ongoing subscriptions to large data streams.
Stop updating the UI, because the user cannot see it anyway.
Persist more complex or less urgent state that you did not already save in onPause, such as cached data or scroll positions.
Example:
override fun onStop() {
super.onStop()
// Release resources that are only needed while visible
}
onStop might never be called in some extreme cases, for example when the process is killed very quickly, so do not depend on it for critical data that must never be lost. Use onPause or onSaveInstanceState for that.
onRestart: Coming Back After Stop
onRestart is called when an activity that was stopped is about to start again. It is always followed by onStart.
In practice, many apps do not need to override onRestart at all. It exists mainly to give you a hook between stopped and started states.
You might use onRestart if you need to:
Differentiate between starting the activity fresh and coming back to it after it was stopped.
Log analytics events when the user returns to your app after a while.
Example:
override fun onRestart() {
super.onRestart()
// Example: log that user returned to the activity
}
Most UI related reinitialization is better done in onStart or onResume rather than here.
onDestroy: Final Cleanup
onDestroy is the final callback that an activity receives before it is destroyed. It is called in two main situations:
The activity is finishing, usually because you called finish() or the user pressed the back button.
The system is temporarily destroying the activity to save space, for example during a configuration change or when reclaiming memory.
You can check whether the activity is finishing with isFinishing.
Use onDestroy to:
Release any remaining resources that have not been released in onStop.
Clean up things that must always be cleaned up when the activity goes away, such as closing a database connection that belongs directly to the activity, or canceling tasks that are tightly bound to the activity lifecycle.
Example:
override fun onDestroy() {
super.onDestroy()
if (isFinishing) {
// Permanent cleanup if the user is leaving this screen for good
}
// General cleanup, release any remaining resources
}
Do not rely on onDestroy to save important user data, because it is not guaranteed to be called in all situations. Use earlier callbacks for critical state saving.
onSaveInstanceState and onRestoreInstanceState
Besides the main lifecycle callbacks, two special methods help you handle transient state, especially during configuration changes such as rotation.
onSaveInstanceState is called before onStop in many cases. It gives you a Bundle where you can put small, serializable pieces of UI state, such as scroll position, text content, or a selected item ID.
onRestoreInstanceState is called after onStart when the activity is being recreated and there is saved state available. It provides the same Bundle that you filled in onSaveInstanceState.
Example:
private var counter: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("counter", counter)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState != null) {
counter = savedInstanceState.getInt("counter", 0)
}
}
You can also override onRestoreInstanceState separately if you want to keep your onCreate logic focused on initial setup.
Use onSaveInstanceState for small UI related state that you want to survive temporary destruction, such as configuration changes. It is not a replacement for permanent storage like databases or files.
Putting It All Together
To understand how callbacks work together, consider these common flows.
When an activity is created for the first time, the typical sequence is:
onCreate → onStart → onResume
When a new activity comes on top and covers yours:
onPause → onStop
If the user returns to your activity from the stopped state:
onRestart → onStart → onResume
If the user presses back and the activity finishes:
onPause → onStop → onDestroy
Seeing these sequences helps you choose where to place your code. Work that is done only once should go in onCreate. Work that you need whenever the activity becomes visible belongs in onStart or onResume. Pauses, releases, and cleanup belong in onPause, onStop, and onDestroy based on how urgent and how heavy they are.
A clear mental model of lifecycle callbacks will guide many design decisions in real Android apps, especially when you start using components like ViewModel, LiveData, and background tasks that must respect the current state of your screens.