Activity Lifecycle is a set of callback methods that Android calls as an Activity transitions between states: creation, visibility, input focus, partial visibility loss, full concealment, and destruction. The system manages the lifecycle of each application screen, starting from the moment onCreate() is called and ending with onDestroy(). Understanding these states is a mandatory requirement for stable Android application operation, since incorrect handling of transitions between methods leads to memory leaks, loss of user data, and unexpected crashes. Learn more about Android architecture in the general article about Android.
Key Takeaways
Activity Lifecycle is a state machine that every screen of an Android application goes through from the moment of creation to complete destruction. The Android system manages this process based on user actions: opening the application, minimizing, screen rotation, responding to an incoming call, switching between applications, and shutdown.
Understanding the lifecycle is essential for every Android developer, since the system can destroy an Activity at any moment when memory is low — and the application must correctly restore its state. According to Google Android Vitals (2025), applications that do not handle state saving in onSaveInstanceState() show 42% more crashes during Activity recreation.
The lifecycle includes six main callback methods: onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy(). Additionally, there is the onRestart() method, which is called before onStart() when an Activity returns from a stopped state. Each method has a strictly defined purpose and execution time — the system calls them sequentially, and the developer can override any of them to implement their own logic.
The cycle can be divided into three key stages: entire lifetime (onCreate → onDestroy), visible lifetime (onStart → onStop), and foreground lifetime (onResume → onPause). Understanding these three levels helps properly distribute initialization and resource release code.
Each lifecycle method performs a strictly defined task. The system calls them in a fixed order, and the developer should only override the methods needed for specific logic. It is not recommended to call lifecycle methods directly — this is handled by the Android Runtime.
A typical sequence when launching an application: onCreate → onStart → onResume. When pressing the Back button: onPause → onStop → onDestroy. When minimizing: onPause → onStop, then upon return: onRestart → onStart → onResume.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
override fun onDestroy() {
super.onDestroy()
}
override fun onRestart() {
super.onRestart()
}
}
Each overridden method must call its super version — without this, the system cannot correctly complete the state transition. This rule is established in the Android Developers documentation and is checked by Android Studio lint rules.
The first level — entire lifetime: the interval between onCreate and onDestroy. One-time initialization and final release of global resources are performed here. The second level — visible lifetime: between onStart and onStop. The Activity is visible on the screen but may be partially covered by another window. The third level — foreground lifetime: between onResume and onPause. The Activity is at the top of the task stack and interacts with the user.
onCreate() — the first and only mandatory method of the Activity lifecycle. It is called by the system once when creating an Activity instance. This method accepts a savedInstanceState: Bundle? parameter, which contains previously saved state if the Activity is being recreated after destruction — for example, during screen rotation.
Inside onCreate, the following tasks are performed: user interface initialization via setContentView() with a layout resource, binding View elements via findViewById(), setting up adapters for RecyclerView and ViewPager, restoring state from savedInstanceState, initializing ViewModel and LiveData, setting up click and gesture listeners. The method should complete as quickly as possible — long operations here block the rendering of the first frame, increasing the application startup time.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
val userNameText: TextView = findViewById(R.id.user_name)
val loadButton: Button = findViewById(R.id.load_button)
if (savedInstanceState != null) {
userNameText.text = savedInstanceState.getString("user_name")
}
loadButton.setOnClickListener {
loadUserProfile()
}
}
If the Activity is being created for the first time, savedInstanceState is null. When recreated after screen rotation, the Bundle contains data saved in onSaveInstanceState(). A null check is standard practice for correctly restoring the UI without losing user-entered data.
onStart() is called immediately after onCreate() or after onRestart(), when the Activity becomes visible to the user. In this state, the Activity is not yet in the foreground and cannot interact with the user, but its user interface is already visible on the screen. For example, when launching an application, the system renders the first frame of the interface between the onStart and onResume calls.
In the onStart method, the following actions are typically performed: starting animations that should run while the Activity is visible; binding broadcast receivers (BroadcastReceiver); connecting to geolocation services and sensors; updating data from ViewModel or Room. Binding to Bound services via bindService() is also performed here if the application uses a client-server architecture within the process.
override fun onStart() {
super.onStart()
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000L,
10f,
locationListener
)
}
override fun onStop() {
super.onStop()
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.removeUpdates(locationListener)
}
Important rule: resources connected in onStart must be released in onStop. This ensures that when the Activity is not visible on the screen, it does not consume battery and system resources. Google Play Store checks applications for LocationListener and other system service leaks when moderating updates.
onResume() — the state in which the Activity is in the foreground and ready to interact with the user. This is the working state of the screen: the system transfers input focus to the Activity, and all touch events, keyboard input, and gestures are directed to this screen. The onResume method is called every time the Activity returns to the foreground — after another Activity finishes, after a dialog box is closed, or after the device is unlocked.
In onResume, the following are performed: resuming animations that were paused in onPause; opening the camera and other exclusive resources; registering sensor listeners (accelerometer, gyroscope); starting timers and stopwatch for the UI; updating screen content with current data. The onResume/onPause pair is used for resources that should be active only when focused — for example, continuous speech recognition or video capture.
override fun onResume() {
super.onResume()
cameraHolder.openCamera()
animator.resume()
sensorManager.registerListener(
stepCounter,
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER),
SensorManager.SENSOR_DELAY_NORMAL
)
}
override fun onPause() {
super.onPause()
cameraHolder.closeCamera()
animator.pause()
sensorManager.unregisterListener(stepCounter)
}
The difference between onStart and onResume is significant: an Activity can be visible (onStart) but not active (onResume) — for example, when a popup dialog or a transparent lock screen is displayed on top of it. It is in onResume, not onStart, that exclusive resources requiring exclusive access should be opened.
onPause() is called when the Activity loses input focus but remains partially visible. Typical scenarios: opening a dialog box, pressing the Recent Apps button, an incoming call, pressing the Home button (in this case, onPause will be followed by onStop). The onPause method is the last reliable place to save data that the user should not lose.
In onPause, the following are performed: saving drafts of emails and input forms to Room or SharedPreferences; stopping animations and video playback; closing the camera and releasing exclusive resources; canceling expensive operations that are not critical in the background. The onPause method should complete in less than 100 milliseconds — the system blocks the transition to the next Activity until onPause returns control, and exceeding the limit leads to ANR (Application Not Responding).
override fun onPause() {
super.onPause()
val editor = SharedPreferences.Manager ...
editor.putString("draft_text", draftEditText.text.toString())
editor.apply()
videoView.pause()
cameraHolder.release()
}
Important: onPause executes on the UI thread, so any blocking operations such as writing to the database via Room with a synchronous query should be replaced with asynchronous ones (coroutines) or executed on a background thread. Use apply() instead of commit() for SharedPreferences — apply writes data asynchronously and does not block the UI thread.
onStop() is called when the Activity stops being visible to the user. This occurs in the following cases: the Activity is completely covered by another Activity; the user pressed the Home button or switched to another application; the Activity is finishing (onDestroy will be called afterward). In the onStop state, the Activity remains in memory and retains all its fields — it is not destroyed, but it is not active either.
In onStop, the following are performed: unregistering BroadcastReceivers registered in onStart; disconnecting from Bound services; releasing LocationListener, SensorListener, and other system listeners; stopping long-running background operations that are not needed when the application is hidden; writing the current UI state to a Bundle via onSaveInstanceState() if this was not done in onPause.
override fun onStop() {
super.onStop()
unregisterReceiver(connectivityReceiver)
unbindService(serviceConnection)
if (isChangingConfigurations()) {
Log.d("Lifecycle", "Activity is recreated due to configuration")
}
}
The system can destroy an Activity in the onStop state without calling onDestroy when memory is low. Therefore, all critical data must be saved before transitioning to onStop. The isChangingConfigurations() flag allows determining whether the onStop call is related to screen rotation — in this case, the Activity will be recreated, not finished.
onDestroy() — the last lifecycle method called before the Activity is completely destroyed. The system calls onDestroy in two cases: the Activity is finishing via finish() or the user presses the Back button; the Activity is being destroyed by the system due to a configuration change (e.g., screen rotation) and will be created again. The onDestroy method allows performing final resource cleanup: unbinding threads and coroutines, closing permanently opened cursors and sockets, and releasing native memory through the NDK.
override fun onDestroy() {
super.onDestroy()
backgroundJob.cancel()
dbHelper.close()
if (isFinishing) {
Log.d("Lifecycle", "Activity is finishing permanently")
} else {
Log.d("Lifecycle", "Activity will be recreated")
}
}
Important note: onDestroy is not guaranteed to be called if the application process is killed by the system (out-of-memory kill). Therefore, relying on onDestroy for data saving is not possible — this task is handled in onPause or onStop. The isFinishing property allows distinguishing between Activity termination via finish() and recreation due to configuration changes.
onRestart() is called before onStart() when an Activity returns from the stopped state (onStop) back to the foreground. This happens when the user reopens the application from the Recent Apps menu or returns to an Activity by pressing Back on a child screen. The onRestart method allows executing logic different from onCreate — for example, updating data that may have changed while the Activity was hidden.
override fun onRestart() {
super.onRestart()
refreshDataFromNetwork()
Log.d("Lifecycle", "Activity is restarting from stack")
}
Typical scenario: the user opened the application, switched to another task, and returned an hour later. In onRestart, the application can check data relevance and, if a lot of time has passed, suggest reloading the content. This improves user experience and reduces the chance of displaying outdated information.
Screen rotation is the most common scenario for Activity recreation. By default, Android destroys the current Activity and creates a new one with every orientation change. If the state is not saved, the user will lose all entered data. Android provides two mechanisms for this: onSaveInstanceState() for serializable data and ViewModel for data that survives configuration changes.
onSaveInstanceState() is called before the Activity is destroyed to save temporary state. The saved data is passed to onCreate via the savedInstanceState parameter and to the onRestoreInstanceState() method, which is called after onStart. The Bundle has a size limit — about 500 KB, so large volumes of data (e.g., bitmaps) are saved via ViewModel.
<!-- AndroidManifest.xml — orientation locking -->
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize" />
Fixing the orientation via android:configChanges prevents Activity recreation, but is considered an antipattern if the application needs to support both orientations. The modern Google recommendation is to use ViewModel in combination with onSaveInstanceState for data that the user enters in the UI.
Fragment has its own lifecycle, similar to Activity, but with additional methods: onAttach, onCreate, onCreateView, onViewCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestroy, onDetach. A Fragment always exists within an Activity, and its lifecycle is tied to the lifecycle of the container Activity. If the Activity is destroyed, the Fragment follows.
The main difference: Fragment manages not only the component state but also the View hierarchy. The onCreateView method returns the Fragment's root View, and onDestroyView destroys this hierarchy. This allows Fragment to survive Activity recreation during screen rotation: the Fragment is retained, and its View is recreated in onCreateView.
class ProfileFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_profile, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val avatarImage: ImageView = view.findViewById(R.id.avatar_image)
loadAvatar(avatarImage)
}
}
Understanding the difference between onCreate and onCreateView is critically important: onCreate is called once per Fragment's lifetime (even when the View is recreated), while onCreateView is called every time the Fragment creates or recreates its View hierarchy. Data initialization is performed in onCreate, while UI binding is done in onViewCreated.
LifecycleObserver — a component of the Android Jetpack library that allows reacting to lifecycle changes without overriding methods in Activity or Fragment. Instead of duplicating code in every lifecycle method, the developer creates a separate class with @OnLifecycleEvent annotations and passes it to lifecycle.addObserver().
Jetpack also provides the LifecycleOwner interface, which is implemented by AppCompatActivity and Fragment. Any object implementing LifecycleOwner can manage LiveData subscriptions, coroutines via lifecycleScope, and WorkManager in relation to the lifecycle. This is a cornerstone of modern Android architecture based on MVVM and Jetpack.
class MyLocationObserver(private val context: Context) : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
startLocationUpdates()
}
override fun onStop(owner: LifecycleOwner) {
stopLocationUpdates()
}
}
// In Activity:
lifecycle.addObserver(MyLocationObserver(this))
Using DefaultLifecycleObserver simplifies testing, reduces code duplication, and makes lifecycle logic reusable across different screens. This is a modern replacement for manually overriding onStart/onStop in every Activity. In Android applications developed by IT Sectr, we apply LifecycleObserver for geolocation, Bluetooth scanning, and analytics — this reduces boilerplate code volume by 30–40%.
Frequently Asked Questions
If you don't call super.onCreate() or any other super lifecycle method, the system will throw a SuperNotCalledException and the application will crash. This is a hard requirement of the Android Runtime — each method must delegate execution to the base class, otherwise the internal state machine cannot transition to the next state.
The Activity is recreated on screen rotation because orientation change is a device configuration change. By default, Android destroys the Activity and creates a new one to load alternative resources (layout-land, values-land). To disable recreation, you can add the android:configChanges attribute to the manifest, but Google recommends using ViewModel to preserve data.
Critical data is saved in onPause(), as this is the last method guaranteed to be called before the application may be killed by the system. After onStop and onDestroy, the system can terminate the process without calling additional methods. For drafts and intermediate data, use SharedPreferences with apply() or Room with coroutines.
onPause is called when the Activity loses focus but remains partially visible (e.g., a dialog box is opened). onStop is called when the Activity is completely hidden from the screen by another Activity or by pressing the Home button. The main practical difference: onPause is the last point for saving data, onStop is the place for releasing listeners and system services that are not needed in the background.
ViewModel is an Android Jetpack component that stores UI data and automatically survives configuration changes (screen rotation). ViewModel is not destroyed when the Activity is recreated: it lives until the LifecycleOwner (Activity or Fragment) is completely finished. This solves the problem of preserving data during screen rotation without using Bundle and onSaveInstanceState. ViewModel is a mandatory element of the MVVM architecture recommended by Google.
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