Fragment Lifecycle — en strikt definierad sekvens av callback-metoder som Android anropar under Fragmentets livstid: från skapande (onAttach) till fullständig borttagning (onDetach). Fragment har en mer komplex livscykel än Activity — den omfattar 11 tillstånd och 7 grundläggande callbacks. Fragment Lifecycle hanteras via FragmentManager och är nära kopplad till livscykeln för Activity som innehåller det. Enligt Google används Fragment i 74% av Android-applikationer som körs på API Level 21+, vilket gör förståelse av Fragment Lifecycle obligatorisk för professionell Android-utveckling. Androids dokumentation om Fragment Lifecycle beskriver alla tillstånd och anropsgarantier.
Huvudpunkter
Fragment Lifecycle är en uppsättning sammanlänkade tillstånd och metoder som varje Fragment-instans går igenom från skapande till förstörelse. Till skillnad från Activity är Fragmentets livscykel bunden till två sammanhang: själva Fragmentet (lever från onAttach till onDetach) och dess View (lever från onCreateView till onDestroyView). Denna separation är en nyckelfunktion hos Fragment, som gör att det kan överleva förstörelse av View vid skärmrotation utan att förstöra själva Fragmentet.
Fullständig sekvens av Fragmentets callbacks:
Enligt Google går det genomsnittliga fragmentet i en modern applikation igenom en fullständig cykel 3–5 gånger per användarsession (på grund av skärmrotationer och navigering). Korrekt hantering av alla faser är grunden för UI-stabilitet.
FragmentManager hanterar Fragmentet genom fem huvudtillstånd, definierade i klassen Fragment.State. Varje tillstånd motsvarar en specifik uppsättning callbacks som har utförts.
| Tillstånd | Betydelse | Utförda callbacks |
|---|---|---|
| INITIALIZED | Fragment skapat, men View finns ännu inte | onAttach, onCreate |
| CREATED | View skapad, men Fragmentet är inte synligt | + onCreateView, onViewCreated |
| STARTED | Fragment synligt, men inte aktivt | + onStart |
| RESUMED | Fragment aktivt, interagerar med användaren | + onResume |
| DESTROYED | Fragment förstört | + onDestroyView, onDestroy, onDetach |
FragmentManager flyttar Fragmentet mellan tillstånd beroende på användaråtgärder och systemhändelser. När Fragmentet läggs till i en behållare går det sekventiellt igenom INITIALIZED → CREATED → STARTED → RESUMED. Vid borttagning — RESUMED → STARTED → CREATED → DESTROYED.
Tillstånd CREATED — speciellt: View kan förstöras (efter onDestroyView), men själva Fragmentet förblir i tillståndet CREATED (efter onDestroyView, före onDestroy). Detta gör att FragmentManager kan behålla Fragmentet i minnet utan View, vilket är nödvändigt för att överleva skärmrotationer.
Fragment Lifecycle och Activity Lifecycle är nära kopplade men har fundamentala skillnader. Fragment lever alltid inuti ett Activity, och dess livscykel beror på värd-Activity, men är inte identisk med den.
| Aspekt | Activity | Fragment |
|---|---|---|
| Antal callbacks | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Separat Lifecycle för View | Nej | Ja (viewLifecycleOwner) |
| Överlever rotation | Nej (förstörs) | Ja (ViewModel + Fragment överlever) |
| Beroende av värd | Nej | Beror på Activity Lifecycle |
| Spara tillstånd | onSaveInstanceState | onSaveInstanceState (fragment) |
| Hantering | System | FragmentManager |
Främsta praktiska skillnad: vid skärmrotation förstörs Activity helt (onDestroy) och återskapas (onCreate). Fragmentet går vid rotation igenom onDestroyView (View förstörs) → onCreateView (View återskapas), men själva Fragmentet och dess ViewModel förblir levande. Detta gör Fragmentet till en idealisk behållare för UI-logik som måste överleva konfigurationsändringar.
Anropsordning vid skärmrotation: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity förstört) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager — den centrala klassen som ansvarar för att lägga till, ta bort, ersätta fragment och hantera deras tillstånd. FragmentManager underhåller BackStack-stacken och garanterar korrekt ordning på callbacks vid transaktioner. Varje Activity och varje nästlat Fragment har sin egen FragmentManager.
Huvudoperationer för FragmentManager:
BackStack — stacken för FragmentManager-transaktioner. När systemknappen ”Tillbaka” trycks in, återställs den senaste transaktionen i BackStack (popBackStack()). Fragment som tagits bort via popBackStack återställs. Om BackStack är tom avslutar tryckning av ”Tillbaka” Activity.
Enligt Google är 78% av problemen med Fragment (duplicering, tomma skärmar, IllegalStateException) relaterade till felaktig användning av FragmentManager. Huvudregel: utför transaktioner via commit() (asynkront) eller commitNow() (synkront) beroende på sammanhang. commit() garanterar korrekt ordning vid flera transaktioner.
Fragment stöder sin egen mekanism för att spara tillstånd via onSaveInstanceState, som fungerar oberoende av Activity. Fragmentet sparar tillstånd i Bundle, som skickas till onCreate och onCreateView vid återställning.
När Fragmentet sparar tillstånd:
Modern metod: använd SavedStateHandle i ViewModel för att spara Fragmentets tillstånd. SavedStateHandle sparar och återställer automatiskt data vid skärmrotation och process death, utan att kräva manuell onSaveInstanceState. Google rekommenderar SavedStateHandle som föredragen metod för att spara UI-tillstånd i Fragment.
setRetainInstance (föråldrad från Fragment 1.3): tidigare kunde Fragmentet bevaras via setRetainInstance(true) vid skärmrotation. Denna metod har ersatts av ViewModel + SavedStateHandle, som fungerar mer tillförlitligt och inte kräver speciell konfiguration.
viewLifecycleOwner — Lifecycle kopplad till Fragmentets View (från onCreateView till onDestroyView). Detta är ett grundläggande viktigt koncept: LiveData/Flow-prenumerationer gjorda via viewLifecycleOwner avbryts automatiskt vid förstörelse av View (onDestroyView), men påverkar inte själva Fragmentet.
Skillnad mellan viewLifecycleOwner och Fragmentets lifecycle:
Varför detta är viktigt: om du prenumererar på LiveData via Fragmentets lifecycle (this), efter onDestroyView förblir prenumerationen aktiv och LiveData kommer att försöka uppdatera null View, vilket orsakar NPE. Prenumeration via viewLifecycleOwner garanterar att inga UI-uppdateringar sker efter onDestroyView.
Regel: i Fragment, använd alltid viewLifecycleOwner för prenumerationer på LiveData, Flow och korutiner relaterade till UI. För ViewModel-korutiner, använd viewModelScope — den är bunden till ViewModel, inte till Fragment.
Visar korrekt initiering av UI och LiveData-prenumeration via 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", "Uppdatera lista: ${users.size} användare")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View förstörd")
}
}
Fragmentet blåser upp layout i onCreateView, konfigurerar UI och prenumererar på LiveData i onViewCreated. Prenumeration via viewLifecycleOwner — ett obligatoriskt krav för att förhindra minnesläckor. onDestroyView loggar förstörelse av View — bekräftelse att Fragmentet överlever skärmrotation.
Visar tillägg av Fragment via FragmentManager i Activity, ersättning med BackStack och återställning.
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", "Ladda användarinformation: $userId")
}
}
Activity använder supportFragmentManager för att hantera fragment. Transaktionen add() med BackStack garanterar att HomeFragment återställs när ”Tillbaka” trycks. openDetail() ersätter nuvarande Fragment med DetailFragment med argument. Kontroll av savedInstanceState == null förhindrar duplicering av fragment vid skärmrotation.
Användning av Flow och StateFlow i Fragment med viewLifecycleOwner för reaktiv UI-uppdatering.
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", "Sökresultat: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Fragmentet använder View Binding för åtkomst till View. Korutinen viewLifecycleOwner.lifecycleScope.launch avbryts automatiskt vid förstörelse av View. Binding nollställs i onDestroyView för att förhindra minnesläckor. StateFlow garanterar att data är aktuell när View återskapas.
Vanliga frågor
onCreateView — skapar och returnerar Fragmentets rot-View. onViewCreated — anropas omedelbart efter att View skapats, garanterar att View är fullt initierad och redo för konfiguration (findViewById, prenumerationer). Google rekommenderar att i onCreateView bara blåsa upp layout och göra all UI-konfiguration i onViewCreated.
onDestroy — Fragmentet är förstört som objekt (ViewModel rensas, korutiner avbryts). onDetach — sista callback, varefter Fragmentet kopplas loss från Activity. Praktiskt taget alla resurser bör frigöras i onDestroyView (View) och onDestroy (Fragment). onDetach — för att rensa referenser till Activity.
Fragmentet försvinner om det inte har lagts till i FragmentManager via en transaktion med bevarande i BackStack eller om Activity inte återställer FragmentManager i onCreate. Lösning: lägg till Fragmentet programmatiskt via supportFragmentManager.beginTransaction().add() i onCreate med kontroll av savedInstanceState == null.
Nej. Fragment är alltid kopplat till ett Activity via FragmentManager. Även vid skärmrotation återskapas Activity och Fragmentet kopplas till det nya Activity. Att skapa ett Fragment utanför Activity är omöjligt — Fragmentets konstruktor kräver en tom konstruktor för återställning av systemet.
Nested fragments (nästlade fragment) — Fragment inuti ett annat Fragment. Används för att bygga komplexa skärmar: flikpaneler, panel med flikar, master-detail. Nästlade fragment hanteras av den underordnade FragmentManager (childFragmentManager). Google rekommenderar att inte överstiga 2 nivåer av nästling för att undvika prestandaproblem.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också