Fragment Lifecycle — přesně definovaná posloupnost metod callback, které Android volá během života Fragmentu: od vytvoření (onAttach) až po úplné odstranění (onDetach). Fragment má složitější životní cyklus než Activity — zahrnuje 11 stavů a 7 základních callbacků. Fragment Lifecycle je spravován pomocí FragmentManager a je úzce spojen s životním cyklem Activity, která jej obsahuje. Podle údajů Google je Fragment používán v 74% Android aplikací běžících na API Level 21+, což činí porozumění Fragment Lifecycle povinným pro profesionální Android vývoj. Dokumentace Android k Fragment Lifecycle popisuje všechny stavy a záruky volání.
Hlavní body
Fragment Lifecycle je soubor vzájemně propojených stavů a metod, kterými prochází každá instance Fragmentu od okamžiku vytvoření až do zničení. Na rozdíl od Activity je životní cyklus Fragmentu vázán na dva kontexty: samotný Fragment (žije od onAttach do onDetach) a jeho View (žije od onCreateView do onDestroyView). Toto oddělení je klíčovou vlastností Fragmentu, která umožňuje přežít zničení View při otočení obrazovky bez zničení samotného Fragmentu.
Úplná posloupnost callbacků Fragmentu:
Podle Google prochází průměrný fragment v moderní aplikaci plný cyklus 3–5krát za uživatelskou relaci (kvůli otáčení obrazovky a navigaci). Správné zpracování všech fází je základem stability UI.
FragmentManager spravuje Fragment prostřednictvím pěti hlavních stavů, definovaných ve třídě Fragment.State. Každý stav odpovídá konkrétní sadě callbacků, které byly provedeny.
| Stav | Význam | Provedené callbacky |
|---|---|---|
| INITIALIZED | Fragment vytvořen, ale View ještě neexistuje | onAttach, onCreate |
| CREATED | View vytvořen, ale Fragment není viditelný | + onCreateView, onViewCreated |
| STARTED | Fragment viditelný, ale neaktivní | + onStart |
| RESUMED | Fragment aktivní, interaguje s uživatelem | + onResume |
| DESTROYED | Fragment zničen | + onDestroyView, onDestroy, onDetach |
FragmentManager přesouvá Fragment mezi stavy v závislosti na akcích uživatele a systémových událostech. Při přidávání Fragmentu do kontejneru postupně prochází INITIALIZED → CREATED → STARTED → RESUMED. Při odebírání — RESUMED → STARTED → CREATED → DESTROYED.
Stav CREATED — zvláštní: View může být zničen (po onDestroyView), ale samotný Fragment zůstává ve stavu CREATED (po onDestroyView, před onDestroy). To umožňuje FragmentManageru uchovat Fragment v paměti bez View, což je nezbytné pro přežití otáčení obrazovky.
Fragment Lifecycle a Activity Lifecycle jsou úzce propojeny, ale mají zásadní rozdíly. Fragment vždy žije uvnitř Activity a jeho životní cyklus závisí na hostitelském Activity, ale není s ním totožný.
| Aspekt | Activity | Fragment |
|---|---|---|
| Počet callbacků | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Samostatný Lifecycle pro View | Ne | Ano (viewLifecycleOwner) |
| Přežije otočení | Ne (je zničen) | Ano (ViewModel + Fragment přežijí) |
| Závislost na hostiteli | Ne | Závisí na Activity Lifecycle |
| Ukládání stavu | onSaveInstanceState | onSaveInstanceState (fragmentový) |
| Správa | Systém | FragmentManager |
Hlavní praktický rozdíl: při otočení obrazovky je Activity zcela zničeno (onDestroy) a znovu vytvořeno (onCreate). Fragment při otočení prochází onDestroyView (View je zničen) → onCreateView (View je znovu vytvořen), ale samotný Fragment a jeho ViewModel zůstávají živé. To činí Fragment ideálním kontejnerem pro UI logiku, která musí přežít změny konfigurace.
Pořadí volání při otočení obrazovky: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity zničeno) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager — centrální třída odpovědná za přidávání, odebírání, nahrazování fragmentů a správu jejich stavů. FragmentManager spravuje zásobník BackStack a garantuje správné pořadí callbacků při transakcích. Každé Activity a každý vnořený Fragment má svůj vlastní FragmentManager.
Hlavní operace FragmentManageru:
BackStack — zásobník transakcí FragmentManageru. Při stisknutí systémového tlačítka „Zpět“ je poslední transakce v BackStack vrácena zpět (popBackStack()). Fragment odstraněný pomocí popBackStack je obnoven. Pokud je BackStack prázdný, stisknutí „Zpět“ ukončí Activity.
Podle Google je 78% problémů s Fragment (duplikace, prázdné obrazovky, IllegalStateException) spojeno s nesprávným používáním FragmentManageru. Hlavní pravidlo: provádějte transakce pomocí commit() (asynchronně) nebo commitNow() (synchronně) v závislosti na kontextu. commit() garantuje správné pořadí při více transakcích.
Fragment podporuje vlastní mechanismus ukládání stavu prostřednictvím onSaveInstanceState, který funguje nezávisle na Activity. Fragment ukládá stav do Bundle, který je předán do onCreate a onCreateView při obnově.
Kdy Fragment ukládá stav:
Moderní přístup: použijte SavedStateHandle ve ViewModel pro ukládání stavu Fragmentu. SavedStateHandle automaticky ukládá a obnovuje data při otočení obrazovky a process death, bez nutnosti ručního onSaveInstanceState. Google doporučuje SavedStateHandle jako preferovaný způsob ukládání stavu UI ve Fragmentu.
setRetainInstance (zastaralé od Fragment 1.3): dříve mohl být Fragment zachován pomocí setRetainInstance(true) při otočení obrazovky. Tento přístup byl nahrazen ViewModel + SavedStateHandle, které fungují spolehlivěji a nevyžadují speciální konfiguraci.
viewLifecycleOwner — Lifecycle připojený k View Fragmentu (od onCreateView do onDestroyView). To je koncepčně velmi důležité: předplatná LiveData/Flow provedená prostřednictvím viewLifecycleOwner se automaticky ruší při zničení View (onDestroyView), ale neovlivňují samotný Fragment.
Rozdíl mezi viewLifecycleOwner a lifecycle Fragmentu:
Proč je to důležité: pokud se přihlásíte k odběru LiveData prostřednictvím lifecycle Fragmentu (this), po onDestroyView zůstává předplatné aktivní a LiveData se bude pokoušet aktualizovat null View, což způsobí NPE. Předplatné prostřednictvím viewLifecycleOwner garantuje, že po onDestroyView nedojde k žádným aktualizacím UI.
Pravidlo: ve Fragmentu vždy používejte viewLifecycleOwner pro předplatná LiveData, Flow a korutin souvisejících s UI. Pro korutiny ViewModel použijte viewModelScope — ten je vázán na ViewModel, nikoli na Fragment.
Demonstruje správnou inicializaci UI a předplatné LiveData prostřednictvím 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", "Aktualizace seznamu: ${users.size} uživatelů")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View zničen")
}
}
Fragment nafukuje layout v onCreateView, konfiguruje UI a přihlašuje se k odběru LiveData v onViewCreated. Předplatné prostřednictvím viewLifecycleOwner — povinný požadavek pro prevenci úniků paměti. onDestroyView loguje zničení View — potvrzení, že Fragment přežije otočení obrazovky.
Demonstruje přidání Fragmentu prostřednictvím FragmentManager v Activity, nahrazení s BackStack a obnovení.
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", "Načítání podrobností uživatele: $userId")
}
}
Activity používá supportFragmentManager pro správu fragmentů. Transakce add() s BackStack garantuje, že při stisknutí „Zpět“ bude HomeFragment obnoven. openDetail() nahrazuje aktuální Fragment DetailFragment s argumenty. Kontrola savedInstanceState == null zabraňuje duplikaci fragmentů při otočení obrazovky.
Použití Flow a StateFlow ve Fragmentu s viewLifecycleOwner pro reaktivní aktualizaci 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", "Výsledky vyhledávání: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Fragment používá View Binding pro přístup k View. Korutina viewLifecycleOwner.lifecycleScope.launch se automaticky ruší při zničení View. Binding se nuluje v onDestroyView pro prevenci úniků paměti. StateFlow garantuje aktuálnost dat při znovuvytvoření View.
Často kladené otázky
onCreateView — vytváří a vrací kořenový View Fragmentu. onViewCreated — volá se ihned po vytvoření View, garantuje, že View je plně inicializován a připraven ke konfiguraci (findViewById, předplatná). Google doporučuje v onCreateView pouze nafukovat layout a veškerou konfiguraci UI dělat v onViewCreated.
onDestroy — Fragment je zničen jako objekt (ViewModel se čistí, korutiny se ruší). onDetach — poslední callback, po kterém se Fragment odpojuje od Activity. Prakticky všechny zdroje by měly být uvolněny v onDestroyView (View) a onDestroy (Fragment). onDetach — pro čištění referencí na Activity.
Fragment zmizí, pokud nebyl přidán do FragmentManager prostřednictvím transakce s uložením v BackStack nebo pokud Activity neobnoví FragmentManager v onCreate. Řešení: přidejte Fragment programově pomocí supportFragmentManager.beginTransaction().add() v onCreate s kontrolou savedInstanceState == null.
Ne. Fragment je vždy připojen k Activity prostřednictvím FragmentManager. I při otočení obrazovky je Activity znovu vytvořeno a Fragment je znovu připojen k novému Activity. Vytvoření Fragmentu mimo Activity je nemožné — konstruktor Fragmentu vyžaduje prázdný konstruktor pro obnovení systémem.
Nested fragments (vnořené fragmenty) — Fragment uvnitř jiného Fragmentu. Používají se pro vytváření složitých obrazovek: panelové karty, panely s kartami, master-detail. Vnořené fragmenty jsou spravovány podřízeným FragmentManager (childFragmentManager). Google doporučuje nepřekračovat 2 úrovně vnoření, aby se předešlo problémům s výkonem.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také