The Complete Android Lifecycle Cheatsheet: Understanding Activity and Fragment Lifecycles

Introduction

The Android lifecycle is a fundamental concept that determines how Android components (particularly Activities and Fragments) behave from creation to destruction. Mastering these lifecycles is crucial for developing stable, efficient, and responsive Android applications. This comprehensive cheatsheet provides a detailed understanding of lifecycle stages, their practical implications, and best practices for managing them effectively. Whether you’re building simple apps or complex applications, proper lifecycle management prevents memory leaks, crashes, and ensures smooth user experiences.

Core Lifecycle Concepts

Lifecycle Components

Android Jetpack’s Lifecycle library provides a structured way to handle lifecycle events:

  • LifecycleOwner: Interface for components with lifecycles (Activities, Fragments)
  • LifecycleObserver: Interface for components that need to observe lifecycle changes
  • Lifecycle.Event: Enumeration of lifecycle events (ON_CREATE, ON_START, etc.)
  • Lifecycle.State: Enumeration of lifecycle states (CREATED, STARTED, etc.)

Lifecycle-Aware Components

Components that automatically adjust their behavior based on the lifecycle state of their hosts:

  • ViewModel: Survives configuration changes, cleared when LifecycleOwner is destroyed
  • LiveData: Only updates observers in active lifecycle states
  • LifecycleService: Service that implements LifecycleOwner
  • ProcessLifecycleOwner: Provides lifecycle for the whole application process

Activity Lifecycle

Lifecycle Stages Overview

Lifecycle MethodDescriptionCommon Use Cases
onCreate()Called when activity is first createdInitialize essential components, set content view, bind views
onStart()Called when activity becomes visibleRegister BroadcastReceivers, initialize UI-related resources
onResume()Called when activity starts interacting with userStart animations, acquire exclusive resources (camera, sensors)
onPause()Called when activity loses focus but is still visiblePause animations, release non-critical resources, save draft data
onStop()Called when activity is no longer visibleRelease UI-related resources, unregister BroadcastReceivers
onDestroy()Called before activity is destroyedClean up resources, unregister listeners, prevent memory leaks
onRestart()Called after onStop() before activity restartsRe-initialize components that were released in onStop()

Visual Representation of Activity Lifecycle

┌─────────────────┐
│    onCreate()   │◄──────────────────┐
└────────┬────────┘                   │
         │                            │
         ▼                            │
┌─────────────────┐                   │
│    onStart()    │◄─────┐            │
└────────┬────────┘      │            │
         │               │            │
         ▼               │            │
┌─────────────────┐      │            │
│    onResume()   │      │            │
└────────┬────────┘      │            │
         │               │            │
Activity running         │            │
         │               │            │
         ▼               │            │
┌─────────────────┐      │            │
│    onPause()    │      │            │
└────────┬────────┘      │            │
         │               │            │
         ▼               │            │
┌─────────────────┐      │            │
│     onStop()    ├──────┘            │
└────────┬────────┘                   │
         │                            │
         ▼                            │
┌─────────────────┐      ┌────────────┴───────┐
│    onDestroy()  │      │     onRestart()    │
└─────────────────┘      └────────────────────┘

Lifecycle Code Example

class MyActivity : AppCompatActivity() {

    private val TAG = "MyActivity"
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG, "onCreate: Activity is created")
        
        // Initialize ViewModels, views, and other components
        // Restore saved instance state if needed
        savedInstanceState?.let {
            val savedText = it.getString("KEY_TEXT")
            // Use restored data
        }
    }
    
    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart: Activity is visible but may not have focus")
        
        // Register BroadcastReceivers
        LocalBroadcastManager.getInstance(this).registerReceiver(
            myReceiver, 
            IntentFilter("MY_ACTION")
        )
    }
    
    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume: Activity has focus and user can interact")
        
        // Start camera, sensors, or animations
        startLocationUpdates()
    }
    
    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: Activity is partially visible but loses focus")
        
        // Pause ongoing operations, save draft data
        pauseAnimations()
        saveDraftData()
    }
    
    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: Activity is no longer visible")
        
        // Unregister receivers, release resources
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver)
        stopLocationUpdates()
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: Activity is being destroyed")
        
        // Final cleanup, prevent memory leaks
        clearReferences()
    }
    
    override fun onRestart() {
        super.onRestart()
        Log.d(TAG, "onRestart: Activity is restarting after being stopped")
        
        // Prepare to start again
        prepareForRestart()
    }
    
    override fun onSaveInstanceState(outState: Bundle) {
        // Save UI state
        outState.putString("KEY_TEXT", textView.text.toString())
        super.onSaveInstanceState(outState)
    }
}

Activity State Transitions

TransitionMethods CalledTriggered By
Activity launchedonCreate() → onStart() → onResume()Starting activity for first time
User navigates awayonPause() → onStop()User presses home, opens another app
User returns to activityonRestart() → onStart() → onResume()User returns to app from recents
Configuration changeonPause() → onStop() → onDestroy() → onCreate() → onStart() → onResume()Screen rotation, language change
Activity finishonPause() → onStop() → onDestroy()User press back, activity calls finish()
System kills process(no lifecycle methods called)System needs resources

Fragment Lifecycle

Fragment Lifecycle Stages

Lifecycle MethodDescriptionCommon Use Cases
onAttach()Called when fragment is attached to activityStore activity context, set up communication with activity
onCreate()Fragment is being createdInitialize non-UI resources, restore saved state
onCreateView()System calls to create view hierarchyInflate layout, initialize view references
onViewCreated()Called after onCreateView()Setup view properties, attach listeners
onStart()Fragment becomes visibleInitialize UI-related components
onResume()Fragment is visible and has focusActivate UI components, start animations
onPause()Fragment visible but loses focusPause animations, save UI state
onStop()Fragment no longer visibleRelease UI resources
onDestroyView()View hierarchy being removedClean up view resources, detach listeners
onDestroy()Fragment being destroyedFinal cleanup of fragment resources
onDetach()Fragment detached from activityClean up activity references

Visual Representation of Fragment Lifecycle

┌─────────────────┐
│    onAttach()   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    onCreate()   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  onCreateView() │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ onViewCreated() │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    onStart()    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    onResume()   │
└────────┬────────┘
         │
Fragment active
         │
         ▼
┌─────────────────┐
│    onPause()    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│     onStop()    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ onDestroyView() │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   onDestroy()   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    onDetach()   │
└─────────────────┘

Fragment Lifecycle Code Example

class MyFragment : Fragment() {

    private val TAG = "MyFragment"
    private var _binding: FragmentMainBinding? = null
    private val binding get() = _binding!!
    
    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.d(TAG, "onAttach: Fragment attached to context")
        
        // Get reference to hosting activity if needed
        if (context is MyListener) {
            listener = context
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "onCreate: Fragment is created")
        
        // Get arguments, initialize non-UI related components
        arguments?.let {
            val param1 = it.getString("ARG_PARAM1")
            // Process arguments
        }
        
        // Retain fragment instance across configuration changes if needed
        // retainInstance = true
    }
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        Log.d(TAG, "onCreateView: Creating view hierarchy")
        
        // Inflate layout
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        return binding.root
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(TAG, "onViewCreated: View hierarchy created")
        
        // Setup views, attach listeners
        binding.button.setOnClickListener {
            // Handle click
        }
        
        // Observe LiveData
        viewModel.data.observe(viewLifecycleOwner) { data ->
            binding.textView.text = data
        }
    }
    
    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart: Fragment visible")
        
        // Initialize UI components
    }
    
    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume: Fragment active and has focus")
        
        // Start animations, acquire resources
    }
    
    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: Fragment loses focus")
        
        // Pause ongoing operations
    }
    
    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: Fragment no longer visible")
        
        // Release UI resources
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(TAG, "onDestroyView: View hierarchy destroyed")
        
        // Clean up view resources
        _binding = null  // Prevent memory leaks
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: Fragment destroyed")
        
        // Final cleanup
    }
    
    override fun onDetach() {
        super.onDetach()
        Log.d(TAG, "onDetach: Fragment detached from activity")
        
        // Clear activity references
        listener = null
    }
    
    companion object {
        fun newInstance(param1: String) =
            MyFragment().apply {
                arguments = Bundle().apply {
                    putString("ARG_PARAM1", param1)
                }
            }
    }
}

Activity vs Fragment Lifecycle

EventActivity MethodsFragment Methods
CreationonCreate()onAttach() → onCreate() → onCreateView() → onViewCreated() → onActivityCreated()
Becoming VisibleonStart()onStart()
Gaining FocusonResume()onResume()
Losing FocusonPause()onPause()
No Longer VisibleonStop()onStop()
DestructiononDestroy()onDestroyView() → onDestroy() → onDetach()

Managing Configuration Changes

Lifecycle During Configuration Changes

When a configuration change occurs (like screen rotation):

  1. Activity: onPause() → onStop() → onDestroy() → onCreate() → onStart() → onResume()
  2. Fragment: onPause() → onStop() → onDestroyView() → onDestroy() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()

Handling Configuration Changes

ApproachImplementationBest For
ViewModelUse ViewModels to retain UI-related dataMost UI data persistence needs
onSaveInstanceState()Override to save lightweight dataSmall amounts of UI state
Retained FragmentSet retainInstance = trueComplex non-UI data (deprecated)
Setting in Manifestandroid:configChanges=”orientation|screenSize”Special cases (use sparingly)

ViewModel Use Example

class MainViewModel : ViewModel() {
    // Data will survive configuration changes
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
    
    init {
        // Will only execute once, not on every configuration change
        fetchUsers()
    }
    
    private fun fetchUsers() {
        viewModelScope.launch {
            // Fetch data and update LiveData
        }
    }
    
    // Called when ViewModel is finally destroyed (not on config changes)
    override fun onCleared() {
        super.onCleared()
        // Clean up resources
    }
}

// In Activity/Fragment
private val viewModel: MainViewModel by viewModels()

onSaveInstanceState Example

// Activity
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putString("KEY_TEXT", binding.editText.text.toString())
    outState.putInt("KEY_POSITION", listPosition)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Restore state
    savedInstanceState?.let {
        binding.editText.setText(it.getString("KEY_TEXT"))
        listPosition = it.getInt("KEY_POSITION")
    }
}

Lifecycle-Aware Components

Implementing LifecycleObserver

class MyLocationObserver(
    private val context: Context,
    private val callback: (Location) -> Unit
) : DefaultLifecycleObserver {
    
    private var locationManager: LocationManager? = null
    
    override fun onStart(owner: LifecycleOwner) {
        // When lifecycle owner starts
        if (hasLocationPermission()) {
            locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            locationManager?.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                1000L,
                10f,
                locationListener
            )
        }
    }
    
    override fun onStop(owner: LifecycleOwner) {
        // When lifecycle owner stops
        locationManager?.removeUpdates(locationListener)
    }
    
    private val locationListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            callback(location)
        }
        
        // Other required methods
    }
}

// In Activity or Fragment
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Create observer and add it to lifecycle
        val locationObserver = MyLocationObserver(this) { location ->
            // Handle location update
        }
        
        // Register observer
        lifecycle.addObserver(locationObserver)
    }
}

ProcessLifecycleOwner

Provides lifecycle for the whole application process:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // Add application-level lifecycle observer
        ProcessLifecycleOwner.get().lifecycle.addObserver(
            object : DefaultLifecycleObserver {
                override fun onStart(owner: LifecycleOwner) {
                    // App moved to foreground
                    Log.d("App", "Application moved to foreground")
                }
                
                override fun onStop(owner: LifecycleOwner) {
                    // App moved to background
                    Log.d("App", "Application moved to background")
                }
            }
        )
    }
}

Common Lifecycle Challenges and Solutions

Memory Leaks

ChallengeSolution
Holding Activity reference in background threadUse WeakReference or implement proper cancellation
Inner classes holding implicit Activity referenceUse static inner classes with WeakReference
Not clearing View referencesSet view references to null in onDestroyView()
Registering listeners without unregisteringAlways unregister in appropriate lifecycle method
Coroutine scope not cancelledUse viewModelScope or lifecycleScope for automatic cancellation

Lifecycle Mismatch

ProblemSolution
Updates after activity/fragment destroyedUse lifecycle-aware components (LiveData)
Async callbacks after onDestroy()Check isAdded() for fragments, use lifecycle state checks
Animation crash after view destroyedCancel animations in onPause() or onStop()
Camera/sensors used after lifecycle endRelease resources in appropriate lifecycle method

Handling Lifecycle Events in Adapters

class MyAdapter(
    private val fragment: Fragment,
    private val items: List<Item>
) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

    init {
        // Register lifecycle observer to handle adapter cleanup
        fragment.viewLifecycleOwner.lifecycle.addObserver(
            object : DefaultLifecycleObserver {
                override fun onDestroy(owner: LifecycleOwner) {
                    // Clean up resources when view is destroyed
                    cancelAllImageLoading()
                }
            }
        )
    }
    
    // Rest of adapter implementation
}

Best Practices for Lifecycle Management

General Best Practices

  • Use Lifecycle Libraries: Leverage ViewModel, LiveData, and lifecycle-aware components
  • Proper Resource Management: Acquire resources in onStart/onResume, release in onPause/onStop
  • Avoid Long Operations in Lifecycle Methods: Keep onCreate() and other methods lightweight
  • Use CoroutineScope Extensions: Use lifecycleScope and viewModelScope for structured concurrency
  • Avoid UI Updates in Background: Only update UI when lifecycle is in appropriate state

Activity-Specific Best Practices

  • Delegate to Fragments: Use activities as containers and navigation controllers
  • Don’t Retain Activity References: Avoid static references to activities
  • Handle Back Navigation Properly: Implement proper back stack behavior
  • Use Single Activity Architecture: Consider using a single activity with multiple fragments

Fragment-Specific Best Practices

  • ViewBinding Cleanup: Set binding to null in onDestroyView()
  • Check isAdded(): Before performing operations that require activity context
  • Use viewLifecycleOwner: For LiveData observers (not ‘this’)
  • Use FragmentManager Transactions Carefully: Be aware of fragment lifecycle during transactions

Testing Lifecycle Components

// Testing Activity Lifecycle using ActivityScenarioRule
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)
    
    @Test
    fun activityRecreation_maintainsState() {
        // Set state
        activityRule.scenario.onActivity { activity ->
            activity.findViewById<EditText>(R.id.editText).setText("Test")
        }
        
        // Recreate activity
        activityRule.scenario.recreate()
        
        // Check if state preserved
        activityRule.scenario.onActivity { activity ->
            assertEquals("Test", activity.findViewById<EditText>(R.id.editText).text.toString())
        }
    }
}

// Testing ViewModel with TestCoroutineDispatcher
@ExperimentalCoroutinesApi
class MyViewModelTest {
    @get:Rule
    val instantExecutorRule = InstantTaskExecutorRule()
    
    private val testDispatcher = StandardTestDispatcher()
    
    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
    }
    
    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }
    
    @Test
    fun `fetchData updates LiveData on success`() = runTest {
        // Create ViewModel
        val viewModel = MyViewModel()
        
        // Fetch data
        viewModel.fetchData()
        
        // Advance time to complete coroutines
        advanceUntilIdle()
        
        // Assert LiveData updated
        assertNotNull(viewModel.data.value)
    }
}

Resources for Further Learning

Official Documentation

Codelab Tutorials

Advanced Resources

Scroll to Top