Fragment Lifecycle è una sequenza rigorosamente definita di metodi callback che Android invoca durante la vita di un Fragment: dalla creazione (onAttach) alla rimozione completa (onDetach). Fragment ha un ciclo di vita più complesso di Activity — include 11 stati e 7 callback principali. Fragment Lifecycle è gestito tramite FragmentManager ed è strettamente legato al ciclo di vita dell'Activity che lo contiene. Secondo Google, Fragment è utilizzato nel 74% delle applicazioni Android che funzionano su API Level 21+, rendendo la comprensione di Fragment Lifecycle obbligatoria per lo sviluppo Android professionale. La documentazione Android su Fragment Lifecycle descrive tutti gli stati e le garanzie di chiamata.
Punti chiave
Fragment Lifecycle è un insieme di stati e metodi interconnessi attraverso cui ogni istanza di Fragment passa dalla creazione alla distruzione. A differenza di Activity, il ciclo di vita di Fragment è legato a due contesti: il Fragment stesso (vive da onAttach a onDetach) e la sua View (vive da onCreateView a onDestroyView). Questa separazione è una caratteristica fondamentale di Fragment, che gli permette di sopravvivere alla distruzione della View durante la rotazione dello schermo senza distruggere il Fragment stesso.
Sequenza completa dei callback di Fragment:
Secondo Google, il frammento medio in un'applicazione moderna passa attraverso il ciclo completo 3–5 volte per sessione utente (a causa di rotazioni dello schermo e navigazione). La gestione corretta di tutte le fasi è il fondamento della stabilità dell'UI.
FragmentManager gestisce Fragment attraverso cinque stati principali, definiti nella classe Fragment.State. Ogni stato corrisponde a un insieme specifico di callback che sono stati eseguiti.
| Stato | Significato | Callback eseguiti |
|---|---|---|
| INITIALIZED | Fragment è stato creato, ma la View non è ancora disponibile | onAttach, onCreate |
| CREATED | La View è stata creata, ma Fragment non è visibile | + onCreateView, onViewCreated |
| STARTED | Fragment è visibile, ma non attivo | + onStart |
| RESUMED | Fragment è attivo, interagisce con l'utente | + onResume |
| DESTROYED | Fragment è stato distrutto | + onDestroyView, onDestroy, onDetach |
FragmentManager sposta Fragment tra gli stati in base alle azioni dell'utente e agli eventi di sistema. Quando si aggiunge Fragment a un contenitore, passa sequenzialmente attraverso INITIALIZED → CREATED → STARTED → RESUMED. Quando si rimuove — RESUMED → STARTED → CREATED → DESTROYED.
Lo stato CREATED è speciale: la View può essere distrutta (dopo onDestroyView), ma il Fragment stesso rimane nello stato CREATED (dopo onDestroyView, prima di onDestroy). Questo permette a FragmentManager di mantenere il Fragment in memoria senza View, necessario per sopravvivere alle rotazioni dello schermo.
Fragment Lifecycle e Activity Lifecycle sono strettamente correlati ma hanno differenze fondamentali. Fragment vive sempre all'interno di un'Activity, e il suo ciclo di vita dipende dall'Activity ospitante, ma non è identico ad essa.
| Aspetto | Activity | Fragment |
|---|---|---|
| Numero di callback | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Lifecycle separato per View | No | Sì (viewLifecycleOwner) |
| Sopravvive alla rotazione | No (viene distrutta) | Sì (ViewModel + Fragment sopravvivono) |
| Dipendenza dall'host | No | Dipende da Activity Lifecycle |
| Salvataggio dello stato | onSaveInstanceState | onSaveInstanceState (a livello di Fragment) |
| Gestione | Sistema | FragmentManager |
La principale differenza pratica: durante la rotazione dello schermo, Activity viene completamente distrutta (onDestroy) e ricreata (onCreate). Fragment durante la rotazione passa attraverso onDestroyView (View distrutta) → onCreateView (View ricreata), ma il Fragment stesso e il suo ViewModel rimangono vivi. Questo rende Fragment un contenitore ideale per la logica UI che deve sopravvivere ai cambiamenti di configurazione.
Ordine delle chiamate durante la rotazione dello schermo: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity distrutta) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager è la classe centrale responsabile dell'aggiunta, rimozione, sostituzione dei fragment e della gestione dei loro stati. FragmentManager mantiene il BackStack e garantisce l'ordine corretto dei callback durante le transazioni. Ogni Activity e ogni Fragment annidato ha il proprio FragmentManager.
Operazioni principali di FragmentManager:
BackStack è lo stack delle transazioni di FragmentManager. Quando si preme il pulsante Indietro di sistema, l'ultima transazione nel BackStack viene annullata (popBackStack()). Un Fragment rimosso tramite popBackStack viene ripristinato. Se BackStack è vuoto, premere Indietro termina l'Activity.
Secondo Google, il 78% dei problemi con Fragment (duplicazione, schermate vuote, IllegalStateException) sono correlati all'uso errato di FragmentManager. La regola principale: eseguire le transazioni tramite commit() (in modo asincrono) o commitNow() (in modo sincrono) a seconda del contesto. commit() garantisce l'ordine corretto in caso di transazioni multiple.
Fragment supporta un proprio meccanismo di salvataggio dello stato tramite onSaveInstanceState, che funziona indipendentemente dall'Activity. Fragment salva lo stato in un Bundle che viene passato a onCreate e onCreateView durante il ripristino.
Quando Fragment salva lo stato:
Approccio moderno: utilizzare SavedStateHandle in ViewModel per salvare lo stato di Fragment. SavedStateHandle salva e ripristina automaticamente i dati durante la rotazione dello schermo e la morte del processo, senza richiedere onSaveInstanceState manuale. Google raccomanda SavedStateHandle come metodo preferito per salvare lo stato dell'UI in Fragment.
setRetainInstance (deprecato da Fragment 1.3): in precedenza Fragment poteva essere conservato tramite setRetainInstance(true) durante la rotazione dello schermo. Questo approccio è stato sostituito da ViewModel + SavedStateHandle, che funzionano in modo più affidabile e non richiedono configurazione speciale.
viewLifecycleOwner è un Lifecycle legato alla View di Fragment (da onCreateView a onDestroyView). Questo è un concetto fondamentalmente importante: le sottoscrizioni a LiveData/Flow effettuate tramite viewLifecycleOwner vengono automaticamente cancellate quando la View viene distrutta (onDestroyView), ma non influenzano il Fragment stesso.
Differenza tra viewLifecycleOwner e lifecycle di Fragment:
Perché è importante: se ci si sottoscrive a LiveData tramite il lifecycle di Fragment (this), dopo onDestroyView la sottoscrizione rimane attiva e LiveData tenterà di aggiornare una View nulla, causando NPE. Sottoscriversi tramite viewLifecycleOwner garantisce che dopo onDestroyView non si verifichino aggiornamenti dell'UI.
Regola: in Fragment, utilizzare sempre viewLifecycleOwner per le sottoscrizioni a LiveData, Flow e coroutine legate all'UI. Per le coroutine di ViewModel, utilizzare viewModelScope — è legato a ViewModel, non a Fragment.
Dimostra l'inizializzazione corretta dell'UI e la sottoscrizione LiveData tramite viewLifecycleOwner.
class UserListFragment : Fragment() {
private val viewModel: UserListViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_user_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button: Button = view.findViewById(R.id.load_button)
button.setOnClickListener { viewModel.loadUsers() }
viewModel.users.observe(viewLifecycleOwner) { users ->
Log.d("UserListFragment", "Aggiornamento elenco: ${users.size} utenti")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View distrutta")
}
}
Fragment infla il layout in onCreateView, configura l'UI e si sottoscrive a LiveData in onViewCreated. La sottoscrizione tramite viewLifecycleOwner è un requisito obbligatorio per prevenire perdite di memoria. onDestroyView registra la distruzione della View — conferma che Fragment sopravvive alla rotazione dello schermo.
Dimostra l'aggiunta di Fragment tramite FragmentManager in Activity, la sostituzione con BackStack e il ripristino.
class HostActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, HomeFragment())
.addToBackStack(null)
.commit()
}
}
fun openDetail(userId: String) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment.newInstance(userId))
.addToBackStack(null)
.commit()
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
super.onBackPressed()
}
}
}
class DetailFragment : Fragment() {
companion object {
fun newInstance(userId: String): DetailFragment {
return DetailFragment().apply {
arguments = Bundle().apply { putString("user_id", userId) }
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userId = arguments?.getString("user_id")
Log.d("DetailFragment", "Caricamento dettagli utente: $userId")
}
}
Activity utilizza supportFragmentManager per gestire i fragment. La transazione add() con BackStack garantisce che alla pressione di Indietro HomeFragment venga ripristinato. openDetail() sostituisce il Fragment corrente con DetailFragment con argomenti. Il controllo di savedInstanceState == null previene la duplicazione dei fragment durante la rotazione dello schermo.
Utilizzo di Flow e StateFlow in Fragment con viewLifecycleOwner per aggiornamenti reattivi dell'UI.
class SearchFragment : Fragment() {
private val viewModel: SearchViewModel by viewModels()
private var binding: FragmentSearchBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSearchBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.searchButton?.setOnClickListener {
viewModel.search(binding?.queryInput?.text.toString())
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.searchResults.collectLatest { results ->
Log.d("SearchFragment", "Risultati ricerca: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Fragment utilizza View Binding per l'accesso alla View. La coroutine viewLifecycleOwner.lifecycleScope.launch viene automaticamente cancellata quando la View viene distrutta. Il binding viene annullato in onDestroyView per prevenire perdite di memoria. StateFlow garantisce l'aggiornamento dei dati alla ricreazione della View.
Domande frequenti
onCreateView crea e restituisce la View radice di Fragment. onViewCreated viene chiamato immediatamente dopo la creazione della View, garantendo che la View sia completamente inizializzata e pronta per la configurazione (findViewById, sottoscrizioni). Google raccomanda di inflare solo il layout in onCreateView e di fare tutta la configurazione dell'UI in onViewCreated.
onDestroy — Fragment viene distrutto come oggetto (ViewModel viene pulito, le coroutine vengono cancellate). onDetach è l'ultimo callback, dopo il quale Fragment si scollega dall'Activity. Praticamente tutte le risorse dovrebbero essere rilasciate in onDestroyView (View) e onDestroy (Fragment). onDetach serve per pulire i riferimenti all'Activity.
Fragment scompare se non è stato aggiunto a FragmentManager tramite una transazione con conservazione nel BackStack o se Activity non ripristina FragmentManager in onCreate. Soluzione: aggiungere Fragment programmaticamente tramite supportFragmentManager.beginTransaction().add() in onCreate con il controllo di savedInstanceState == null.
No. Fragment è sempre legato a un'Activity tramite FragmentManager. Anche durante la rotazione dello schermo, Activity viene ricreata e Fragment viene ricollegato alla nuova Activity. Creare un Fragment al di fuori di un'Activity è impossibile — il costruttore di Fragment richiede un costruttore vuoto per il ripristino da parte del sistema.
I fragment annidati (nested fragments) sono Fragments all'interno di un altro Fragment. Vengono utilizzati per costruire schermate complesse: pannelli a schede, pannelli con linguette, master-detail. I fragment annidati sono gestiti dal FragmentManager figlio (childFragmentManager). Google raccomanda di non superare 2 livelli di annidamento per evitare problemi di prestazioni.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche