Fragment Lifecycle — μια αυστηρά καθορισμένη ακολουθία μεθόδων callback που καλεί το Android κατά τη διάρκεια ζωής ενός Fragment: από τη δημιουργία (onAttach) έως την πλήρη αφαίρεση (onDetach). Το Fragment έχει πιο σύνθετο κύκλο ζωής από το Activity — περιλαμβάνει 11 καταστάσεις και 7 βασικά callback. Το Fragment Lifecycle διαχειρίζεται μέσω του FragmentManager και συνδέεται στενά με τον κύκλο ζωής του Activity που το περιέχει. Σύμφωνα με τα δεδομένα της Google, το Fragment χρησιμοποιείται στο 74% των εφαρμογών Android που λειτουργούν σε API Level 21+, καθιστώντας την κατανόηση του Fragment Lifecycle υποχρεωτική για επαγγελματική ανάπτυξη Android. Η τεκμηρίωση Android για το Fragment Lifecycle περιγράφει όλες τις καταστάσεις και τις εγγυήσεις κλήσης.
Κύρια σημεία
Fragment Lifecycle είναι ένα σύνολο αλληλένδετων καταστάσεων και μεθόδων από τις οποίες περνά κάθε στιγμιότυπο Fragment από τη στιγμή της δημιουργίας έως την καταστροφή. Σε αντίθεση με το Activity, ο κύκλος ζωής του Fragment συνδέεται με δύο περιβάλλοντα: το ίδιο το Fragment (ζει από onAttach έως onDetach) και το View του (ζει από onCreateView έως onDestroyView). Αυτός ο διαχωρισμός είναι ένα βασικό χαρακτηριστικό του Fragment, που του επιτρέπει να επιβιώνει από την καταστροφή του View κατά την περιστροφή οθόνης χωρίς να καταστρέφεται το ίδιο το Fragment.
Πλήρης ακολουθία callback του Fragment:
Σύμφωνα με την Google, το μέσο fragment σε μια σύγχρονη εφαρμογή περνά τον πλήρη κύκλο 3–5 φορές ανά συνεδρία χρήστη (λόγω περιστροφών οθόνης και πλοήγησης). Η σωστή διαχείριση όλων των φάσεων είναι η βάση της σταθερότητας του UI.
Το FragmentManager διαχειρίζεται το Fragment μέσω πέντε βασικών καταστάσεων, που ορίζονται στην κλάση Fragment.State. Κάθε κατάσταση αντιστοιχεί σε ένα συγκεκριμένο σύνολο callback που έχουν εκτελεστεί.
| Κατάσταση | Σημασία | Εκτελεσμένα callback |
|---|---|---|
| INITIALIZED | Fragment δημιουργήθηκε, αλλά View δεν υπάρχει ακόμα | onAttach, onCreate |
| CREATED | View δημιουργήθηκε, αλλά Fragment δεν είναι ορατό | + onCreateView, onViewCreated |
| STARTED | Fragment ορατό, αλλά μη ενεργό | + onStart |
| RESUMED | Fragment ενεργό, αλληλεπιδρά με τον χρήστη | + onResume |
| DESTROYED | Fragment καταστράφηκε | + onDestroyView, onDestroy, onDetach |
Το FragmentManager μετακινεί το Fragment μεταξύ καταστάσεων ανάλογα με τις ενέργειες του χρήστη και τα συμβάντα συστήματος. Κατά την προσθήκη του Fragment σε ένα δοχείο, περνά διαδοχικά από INITIALIZED → CREATED → STARTED → RESUMED. Κατά την αφαίρεση — RESUMED → STARTED → CREATED → DESTROYED.
Η κατάσταση CREATED — ειδική: το View μπορεί να καταστραφεί (μετά το onDestroyView), αλλά το ίδιο το Fragment παραμένει στην κατάσταση CREATED (μετά το onDestroyView, πριν το onDestroy). Αυτό επιτρέπει στο FragmentManager να διατηρεί το Fragment στη μνήμη χωρίς View, το οποίο είναι απαραίτητο για την επιβίωση των περιστροφών οθόνης.
Το Fragment Lifecycle και το Activity Lifecycle συνδέονται στενά, αλλά έχουν θεμελιώδεις διαφορές. Το Fragment ζει πάντα μέσα σε ένα Activity και ο κύκλος ζωής του εξαρτάται από το Activity-οικοδεσπότη, αλλά δεν είναι ταυτόσημος με αυτό.
| Πτυχή | Activity | Fragment |
|---|---|---|
| Αριθμός callback | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Ξεχωριστός Lifecycle για View | Όχι | Ναι (viewLifecycleOwner) |
| Επιβιώνει περιστροφή | Όχι (καταστρέφεται) | Ναι (ViewModel + Fragment επιβιώνουν) |
| Εξάρτηση από οικοδεσπότη | Όχι | Εξαρτάται από Activity Lifecycle |
| Αποθήκευση κατάστασης | onSaveInstanceState | onSaveInstanceState (fragment) |
| Διαχείριση | Σύστημα | FragmentManager |
Κύρια πρακτική διαφορά: κατά την περιστροφή οθόνης, το Activity καταστρέφεται πλήρως (onDestroy) και δημιουργείται ξανά (onCreate). Το Fragment κατά την περιστροφή περνά από onDestroyView (το View καταστρέφεται) → onCreateView (το View δημιουργείται ξανά), αλλά το ίδιο το Fragment και το ViewModel του παραμένουν ζωντανά. Αυτό καθιστά το Fragment ιδανικό δοχείο για λογική UI που πρέπει να επιβιώνει από αλλαγές διαμόρφωσης.
Σειρά κλήσεων κατά την περιστροφή οθόνης: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity καταστράφηκε) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager — η κεντρική κλάση υπεύθυνη για την προσθήκη, αφαίρεση, αντικατάσταση fragment και διαχείριση των καταστάσεών τους. Το FragmentManager διατηρεί τη στοίβα BackStack και εγγυάται τη σωστή σειρά callback στις συναλλαγές. Κάθε Activity και κάθε ένθετο Fragment έχει το δικό του FragmentManager.
Κύριες λειτουργίες του FragmentManager:
BackStack — στοίβα συναλλαγών του FragmentManager. Όταν πατηθεί το κουμπί συστήματος “Πίσω”, η τελευταία συναλλαγή στο BackStack αναιρείται (popBackStack()). Το Fragment που αφαιρέθηκε μέσω popBackStack αποκαθίσταται. Εάν το BackStack είναι άδειο, το πάτημα “Πίσω” τερματίζει το Activity.
Σύμφωνα με την Google, 78% των προβλημάτων με Fragment (διπλασιασμός, κενές οθόνες, IllegalStateException) σχετίζονται με λανθασμένη χρήση του FragmentManager. Κύριος κανόνας: εκτελείτε συναλλαγές μέσω commit() (ασύγχρονα) ή commitNow() (σύγχρονα) ανάλογα με το περιβάλλον. Το commit() εγγυάται τη σωστή σειρά σε πολλαπλές συναλλαγές.
Το Fragment υποστηρίζει τον δικό του μηχανισμό αποθήκευσης κατάστασης μέσω onSaveInstanceState, ο οποίος λειτουργεί ανεξάρτητα από το Activity. Το Fragment αποθηκεύει κατάσταση στο Bundle, το οποίο μεταβιβάζεται στα onCreate και onCreateView κατά την επαναφορά.
Πότε το Fragment αποθηκεύει κατάσταση:
Σύγχρονη προσέγγιση: χρησιμοποιήστε SavedStateHandle στο ViewModel για αποθήκευση κατάστασης Fragment. Το SavedStateHandle αποθηκεύει και επαναφέρει αυτόματα δεδομένα κατά την περιστροφή οθόνης και process death, χωρίς να απαιτεί χειροκίνητο onSaveInstanceState. Η Google συνιστά το SavedStateHandle ως προτιμώμενο τρόπο αποθήκευσης κατάστασης UI στο Fragment.
setRetainInstance (ξεπερασμένο από Fragment 1.3): προηγουμένως το Fragment μπορούσε να διατηρηθεί μέσω setRetainInstance(true) κατά την περιστροφή οθόνης. Αυτή η προσέγγιση αντικαταστάθηκε από ViewModel + SavedStateHandle, που λειτουργούν πιο αξιόπιστα και δεν απαιτούν ειδική διαμόρφωση.
viewLifecycleOwner — Lifecycle συνδεδεμένο με το View του Fragment (από onCreateView έως onDestroyView). Αυτή είναι μια θεμελιωδώς σημαντική έννοια: οι εγγραφές LiveData/Flow που γίνονται μέσω viewLifecycleOwner ακυρώνονται αυτόματα κατά την καταστροφή του View (onDestroyView), αλλά δεν επηρεάζουν το ίδιο το Fragment.
Διαφορά μεταξύ viewLifecycleOwner και lifecycle του Fragment:
Γιατί είναι σημαντικό: αν εγγραφείτε στο LiveData μέσω του lifecycle του Fragment (this), μετά το onDestroyView η εγγραφή παραμένει ενεργή και το LiveData θα προσπαθήσει να ενημερώσει το null View, προκαλώντας NPE. Η εγγραφή μέσω viewLifecycleOwner εγγυάται ότι μετά το onDestroyView δεν θα γίνουν ενημερώσεις UI.
Κανόνας: στο Fragment, χρησιμοποιείτε πάντα viewLifecycleOwner για εγγραφές LiveData, Flow και coroutine που σχετίζονται με το UI. Για coroutine ViewModel, χρησιμοποιείτε viewModelScope — αυτό συνδέεται με το ViewModel, όχι με το Fragment.
Δείχνει σωστή αρχικοποίηση UI και εγγραφή LiveData μέσω 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", "Ενημέρωση λίστας: ${users.size} χρήστες")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View καταστράφηκε")
}
}
Το Fragment φουσκώνει το layout στο onCreateView, ρυθμίζει το UI και εγγράφεται στο LiveData στο onViewCreated. Η εγγραφή μέσω viewLifecycleOwner — υποχρεωτική απαίτηση για την πρόληψη διαρροών μνήμης. Το onDestroyView καταγράφει την καταστροφή του View — επιβεβαίωση ότι το Fragment επιβιώνει από την περιστροφή οθόνης.
Δείχνει προσθήκη Fragment μέσω FragmentManager στο Activity, αντικατάσταση με BackStack και επαναφορά.
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", "Φόρτωση λεπτομερειών χρήστη: $userId")
}
}
Το Activity χρησιμοποιεί supportFragmentManager για τη διαχείριση fragment. Η συναλλαγή add() με BackStack εγγυάται ότι όταν πατηθεί “Πίσω” το HomeFragment θα αποκατασταθεί. Η openDetail() αντικαθιστά το τρέχον Fragment με DetailFragment με ορίσματα. Ο έλεγχος savedInstanceState == null αποτρέπει τον διπλασιασμό fragment κατά την περιστροφή οθόνης.
Χρήση Flow και StateFlow σε Fragment με viewLifecycleOwner για αντιδραστική ενημέρωση 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", "Αποτελέσματα αναζήτησης: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Το Fragment χρησιμοποιεί View Binding για πρόσβαση στο View. Το coroutine viewLifecycleOwner.lifecycleScope.launch ακυρώνεται αυτόματα κατά την καταστροφή του View. Το Binding μηδενίζεται στο onDestroyView για πρόληψη διαρροών μνήμης. Το StateFlow εγγυάται την επικαιρότητα των δεδομένων κατά την αναδημιουργία του View.
Συχνές ερωτήσεις
onCreateView — δημιουργεί και επιστρέφει το ριζικό View του Fragment. onViewCreated — καλείται αμέσως μετά τη δημιουργία του View, εγγυάται ότι το View είναι πλήρως αρχικοποιημένο και έτοιμο για ρύθμιση (findViewById, εγγραφές). Η Google συνιστά στο onCreateView να φουσκώνετε μόνο το layout, και όλη τη ρύθμιση UI να την κάνετε στο onViewCreated.
onDestroy — το Fragment καταστράφηκε ως αντικείμενο (ViewModel καθαρίζεται, coroutine ακυρώνονται). onDetach — το τελευταίο callback, μετά το οποίο το Fragment αποσυνδέεται από το Activity. Πρακτικά όλοι οι πόροι πρέπει να απελευθερωθούν στο onDestroyView (View) και onDestroy (Fragment). onDetach — για καθαρισμό αναφορών στο Activity.
Το Fragment εξαφανίζεται αν δεν προστέθηκε στο FragmentManager μέσω συναλλαγής με αποθήκευση στο BackStack ή αν το Activity δεν επαναφέρει το FragmentManager στο onCreate. Λύση: προσθέστε το Fragment προγραμματιστικά μέσω supportFragmentManager.beginTransaction().add() στο onCreate με έλεγχο savedInstanceState == null.
Όχι. Το Fragment είναι πάντα συνδεδεμένο με ένα Activity μέσω του FragmentManager. Ακόμη και κατά την περιστροφή οθόνης, το Activity αναδημιουργείται και το Fragment επανασυνδέεται στο νέο Activity. Η δημιουργία Fragment εκτός Activity είναι αδύνατη — ο κατασκευαστής του Fragment απαιτεί κενό κατασκευαστή για επαναφορά από το σύστημα.
Nested fragments (ένθετα fragment) — Fragment μέσα σε άλλο Fragment. Χρησιμοποιούνται για τη δημιουργία σύνθετων οθονών: πίνακες καρτελών, πίνακες με καρτέλες, master-detail. Τα ένθετα fragment διαχειρίζονται από το θυγατρικό FragmentManager (childFragmentManager). Η Google συνιστά να μην υπερβαίνετε τα 2 επίπεδα ένθεσης για αποφυγή προβλημάτων απόδοσης.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης