Skip to content
beginner Phase 2 · Android Fundamentals

Fragment Lifecycle

Understand Fragment lifecycle, communication with Activity, and fragment transactions.

50m
3 problems
Topic Progress 0%

Fragment Lifecycle Overview

The Fragment Lifecycle

A Fragment is a reusable portion of UI that lives inside an Activity. Its lifecycle is tied to the host Activity — fragments can be added, removed, replaced, and reordered while the activity is running.

Lifecycle Callbacks

class HomeFragment : Fragment() {

    override fun onAttach(context: Context) {
        super.onAttach(context)
        // Fragment attached to activity — validate host implements required interfaces
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Fragment created — initialize non-UI components
        // Retain instance here across configuration changes
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
        // Inflate the layout — this is where you set up the view hierarchy
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // View created — safe to access view references here
        // Use viewLifecycleOwner for LiveData observation
    }

    override fun onStart() {
        super.onStart()
        // Fragment visible
    }

    override fun onResume() {
        super.onResume()
        // Fragment in foreground
    }

    override fun onPause() {
        super.onPause()
        // Fragment partially obscured
    }

    override fun onStop() {
        super.onStop()
        // Fragment not visible
    }

    override fun onDestroyView() {
        super.onDestroyView()
        // View destroyed — null out view references to prevent leaks
    }

    override fun onDestroy() {
        super.onDestroy()
        // Fragment destroyed — clean up non-view resources
    }

    override fun onDetach() {
        super.onDetach()
        // Fragment detached from activity
    }
}

Key Differences from Activity Lifecycle

Fragments have three additional callbacks compared to Activities:

  1. onAttach — connected to the host activity
  2. onCreateView — inflating the UI layout
  3. onDestroyView — view destroyed but fragment still alive

And one extra state: onViewCreated — called after onCreateView when the view is ready.

Configuration Changes

Fragments have a special behavior during configuration changes: onDestroyViewonCreateViewonViewCreated fires (view is recreated), but the fragment instance itself survives. The onDestroyonCreate pair only fires when the fragment is truly removed.

Use viewLifecycleOwner instead of this when observing LiveData to avoid observing stale view references after configuration changes.

Fragment Communication

Fragment Communication

Fragments should never directly communicate with each other. Instead, communicate through the host Activity or use shared ViewModel.

Option 1: Shared ViewModel

class SharedViewModel : ViewModel() {
    private val _selectedItem = MutableLiveData<String>()
    val selectedItem: LiveData<String> = _selectedItem

    fun selectItem(item: String) {
        _selectedItem.value = item
    }
}

class ListFragment : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        sharedViewModel.selectedItem.observe(viewLifecycleOwner) { item ->
            // Respond to selection from detail fragment
        }
    }
}

class DetailFragment : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()

    fun onItemSelected(item: String) {
        sharedViewModel.selectItem(item)
    }
}

The ViewModel is scoped to the Activity, so both fragments share the same instance.

Option 2: Fragment Result API

For one-shot communication (not continuous observation):

// Sending fragment
class SenderFragment : Fragment() {
    fun sendData() {
        setFragmentResult("requestKey", bundleOf("dataKey" to "Hello"))
    }
}

// Receiving fragment
class ReceiverFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setFragmentResultListener("requestKey") { _, bundle ->
            val data = bundle.getString("dataKey")
            // Handle data
        }
    }
}

Option 3: Interface Callbacks (Old Pattern)

class ListFragment : Fragment() {
    private var listener: OnItemSelectedListener? = null

    interface OnItemSelectedListener {
        fun onItemSelected(item: String)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is OnItemSelectedListener) {
            listener = context
        } else {
            throw RuntimeException("${context} must implement OnItemSelectedListener")
        }
    }

    override fun onDetach() {
        super.onDetach()
        listener = null
    }
}

This pattern is fragile — the host activity must implement the interface. Prefer shared ViewModel for modern apps.

Fragment Transactions

Fragment Transactions

FragmentManager manages adding, replacing, and removing fragments. All changes happen through transactions:

Basic Transactions

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.fragmentContainer, HomeFragment())
                .commit()
        }
    }
}

Always check savedInstanceState == null before committing the initial fragment — otherwise you'll get duplicate fragments after configuration changes.

Adding to Back Stack

supportFragmentManager.beginTransaction()
    .replace(R.id.fragmentContainer, DetailFragment())
    .addToBackStack(null)  // User can press Back to return
    .commit()

Fragment Reuse and Arguments

Don't create new fragment instances — use the newInstance pattern with arguments:

class DetailFragment : Fragment() {
    companion object {
        fun newInstance(itemId: String): DetailFragment {
            return DetailFragment().apply {
                arguments = bundleOf("itemId" to itemId)
            }
        }
    }

    private val itemId: String by lazy {
        requireArguments().getString("itemId", "")
    }
}

// Usage
supportFragmentManager.beginTransaction()
    .replace(R.id.fragmentContainer, DetailFragment.newInstance("123"))
    .addToBackStack(null)
    .commit()

View Binding in Fragments

class HomeFragment : Fragment() {
    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null  // Prevent memory leak
    }
}

Null out the binding in onDestroyView to prevent memory leaks — the view is destroyed but the fragment may still be alive.

Quiz

1. Which callback is unique to Fragments (not in Activity)?

Question 1 options

2. Why should you use viewLifecycleOwner for observing LiveData in fragments?

Question 2 options

3. What is the correct way to pass data between fragments?

Question 3 options

4. Why must you null out view binding in onDestroyView?

Question 4 options

Flashcards

Question

What are the three additional lifecycle callbacks in Fragments compared to Activities?

Answer

onAttach (connected to activity), onCreateView (inflate layout), onDestroyView (view destroyed).

Question

What is the recommended way for fragments to communicate?

Answer

Use a shared ViewModel (scoped to the activity) or the Fragment Result API. Avoid direct fragment-to-fragment communication.

Question

Why check savedInstanceState == null before adding the first fragment?

Answer

Otherwise the fragment is added again after configuration changes, causing duplicates.

Question

What pattern should you use to pass data to a fragment?

Answer

Use arguments Bundle via a newInstance companion function. Never pass data through constructors.

Revision Notes

Key Takeaways

  • 1. Fragment lifecycle is more complex than Activity lifecycle with additional view-specific callbacks
  • 2. viewLifecycleOwner prevents stale view references after configuration changes
  • 3. Shared ViewModel is the modern way for fragment-to-fragment communication
  • 4. Always null out view binding in onDestroyView to prevent memory leaks
  • 5. Check savedInstanceState == null to avoid duplicate fragments on rotation

Interview Tips

  • Know the complete fragment lifecycle and how it differs from Activity
  • Explain why viewLifecycleOwner is necessary for LiveData observation
  • Discuss the trade-offs between shared ViewModel, Fragment Result API, and interface callbacks
  • Be ready to explain how to handle fragment back stack properly

Cheat Sheet

Fragment Lifecycle Cheat Sheet

Callbacks (in order):

  1. onAttach — connected to activity
  2. onCreate — initialize non-UI
  3. onCreateView — inflate layout
  4. onViewCreated — view ready
  5. onStart — visible
  6. onResume — foreground
  7. onPause — partially obscured
  8. onStop — not visible
  9. onDestroyView — view destroyed
  10. onDestroy — fragment destroyed
  11. onDetach — disconnected

Key Rules:

  • Use viewLifecycleOwner for LiveData
  • Null out binding in onDestroyView
  • Check savedInstanceState == null for initial fragment
  • Use newInstance pattern with arguments
  • Never communicate directly between fragments