Fragment Lifecycle: podstawy, metody onCreateView onViewCreated

Autor: IT Sectr Opublikowano: 2026-03-04 Czas czytania: 12 min

Fragment Lifecycle — ściśle określona sekwencja metod callback, które Android wywołuje w ciągu życia Fragment: od utworzenia (onAttach) do całkowitego usunięcia (onDetach). Fragment ma bardziej złożony cykl życia niż Activity — obejmuje 11 stanów i 7 podstawowych callbacków. Fragment Lifecycle jest zarządzany przez FragmentManager i ściśle powiązany z cyklem życia zawierającego go Activity. Według danych Google, Fragment jest używany w 74% aplikacji Android działających na API Level 21+, co czyni zrozumienie Fragment Lifecycle obowiązkowym dla profesjonalnego programowania Android. Dokumentacja Android na temat Fragment Lifecycle opisuje wszystkie stany i gwarancje wywołania.

Najważniejsze

  • Fragment Lifecycle obejmuje 7 callbacków: onAttach, onCreate, onCreateView, onViewCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestroy, onDetach.
  • FragmentManager zarządza stanami Fragment i gwarantuje prawidłową kolejność wywołań przy transakcjach.
  • onCreateView i onViewCreated — kluczowe metody do tworzenia i konfiguracji UI Fragment.
  • Fragment może przetrwać swoje Activity (przy obrocie ekranu) i przywracać stan przez onSaveInstanceState.
  • viewLifecycleOwner — oddzielny Lifecycle dla View Fragment, niszczony w onDestroyView.

Fragment Lifecycle: podstawy cyklu życia

Fragment Lifecycle to zestaw powiązanych stanów i metod, przez które przechodzi każda instancja Fragment od momentu utworzenia do zniszczenia. W przeciwieństwie do Activity, cykl życia Fragment jest powiązany z dwoma kontekstami: sam Fragment (żyje od onAttach do onDetach) i jego View (żyje od onCreateView do onDestroyView). To rozdzielenie jest kluczową cechą Fragment, pozwalającą przetrwać zniszczenie View przy obrocie ekranu bez niszczenia samego Fragment.

Pełna sekwencja callbacków Fragment:

  • onAttach(Context) — Fragment przywiązuje się do Activity. Wywoływany jako pierwszy. Context — Activity-host.
  • onCreate(Bundle) — Fragment jest inicjalizowany. Tutaj tworzone są ViewModel, konfigurowane adaptery.
  • onCreateView(LayoutInflater, ViewGroup, Bundle) — tworzona jest hierarchia View Fragment. Zwraca główny View.
  • onViewCreated(View, Bundle) — View został utworzony. Tutaj konfigurowane są elementy UI, subskrypcje LiveData.
  • onStart() — Fragment jest widoczny. Uruchamiane są animacje, rejestrowane czujniki.
  • onResume() — Fragment jest aktywny, współdziała z użytkownikiem.
  • onPause() — Fragment traci fokus. Zatrzymywane są animacje.
  • onStop() — Fragment jest niewidoczny. Zwalniane są niekrytyczne zasoby.
  • onDestroyView() — hierarchia View jest niszczona. Referencje do View są zerowane.
  • onDestroy() — Fragment jest niszczony. Anulowane są korutyny nie w viewModelScope.
  • onDetach() — Fragment odłącza się od Activity. Ostateczne czyszczenie.

Według Google, średni fragment w nowoczesnej aplikacji przechodzi pełny cykl 3–5 razy na sesję użytkownika (z powodu obrotów ekranu i nawigacji). Prawidłowe obsłużenie wszystkich faz jest podstawą stabilności UI.

Stany Fragment: od INITIALIZED do DESTROYED

FragmentManager zarządza Fragment przez pięć głównych stanów, zdefiniowanych w klasie Fragment.State. Każdy stan odpowiada określonemu zestawowi callbacków, które zostały wykonane.

StanZnaczenieWykonane callbacki
INITIALIZEDFragment utworzony, ale View jeszcze nie maonAttach, onCreate
CREATEDView utworzony, ale Fragment nie jest widoczny+ onCreateView, onViewCreated
STARTEDFragment widoczny, ale nieaktywny+ onStart
RESUMEDFragment aktywny, współdziała z użytkownikiem+ onResume
DESTROYEDFragment zniszczony+ onDestroyView, onDestroy, onDetach

FragmentManager przenosi Fragment między stanami w zależności od działań użytkownika i zdarzeń systemowych. Przy dodawaniu Fragment do kontenera przechodzi kolejno INITIALIZED → CREATED → STARTED → RESUMED. Przy usuwaniu — RESUMED → STARTED → CREATED → DESTROYED.

Stan CREATED — szczególny: View może być zniszczony (po onDestroyView), ale sam Fragment pozostaje w stanie CREATED (po onDestroyView, przed onDestroy). Pozwala to FragmentManager zachować Fragment w pamięci bez View, co jest niezbędne do przetrwania obrotów ekranu.

Różnica między Fragment Lifecycle a Activity Lifecycle

Fragment Lifecycle i Activity Lifecycle są ściśle powiązane, ale mają fundamentalne różnice. Fragment zawsze żyje wewnątrz Activity, a jego cykl życia zależy od Activity-gospodarza, ale nie jest mu identyczny.

AspektActivityFragment
Liczba callbacków7 (onCreate … onDestroy)11 (onAttach … onDetach)
Oddzielny Lifecycle dla ViewNieTak (viewLifecycleOwner)
Przetrwa obrótNie (niszczony)Tak (ViewModel + Fragment przetrwają)
Zależność od hostaNieZależy od Activity Lifecycle
Zapisywanie stanuonSaveInstanceStateonSaveInstanceState (fragmentowe)
ZarządzanieSystemFragmentManager

Główna praktyczna różnica: przy obrocie ekranu Activity jest niszczone całkowicie (onDestroy) i tworzone na nowo (onCreate). Fragment przy obrocie przechodzi przez onDestroyView (View jest niszczony) → onCreateView (View jest tworzony na nowo), ale sam Fragment i jego ViewModel pozostają żywe. To czyni Fragment idealnym kontenerem dla logiki UI, która powinna przetrwać zmiany konfiguracyjne.

Kolejność wywołań przy obrocie ekranu: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity zniszczona) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.

FragmentManager: zarządzanie stanami i transakcjami

FragmentManager — centralna klasa odpowiedzialna za dodawanie, usuwanie, zastępowanie fragmentów i zarządzanie ich stanami. FragmentManager prowadzi stos BackStack i gwarantuje prawidłową kolejność callbacków przy transakcjach. Każde Activity i każdy zagnieżdżony Fragment mają swój FragmentManager.

Główne operacje FragmentManager:

  • beginTransaction() — otwiera transakcję dla grupy operacji.
  • add() — dodaje Fragment do kontenera. Fragment przechodzi pełny lifecycle do RESUMED.
  • replace() — zastępuje bieżący Fragment nowym. equals remove() + add().
  • remove() — usuwa Fragment. Fragment przechodzi lifecycle od RESUMED do DESTROYED.
  • hide()/show() — ukrywa/pokazuje Fragment bez niszczenia View. Fragment przechodzi do STARTED przy hide, z powrotem do RESUMED przy show.
  • detach()/attach() — odłącza/przyłącza Fragment. detach niszczy View (onDestroyView), attach tworzy na nowo (onCreateView).
  • addToBackStack() — dodaje transakcję do BackStack dla umożliwienia nawigacji „Wstecz".

BackStack — stos transakcji FragmentManager. Po naciśnięciu systemowego przycisku „Wstecz" ostatnia transakcja w BackStack jest cofana (popBackStack()). Fragment usunięty przez popBackStack jest przywracany. Jeśli BackStack jest pusty, naciśnięcie „Wstecz" kończy Activity.

Według Google, 78% problemów z Fragment (duplikowanie, puste ekrany, IllegalStateException) jest związanych z nieprawidłowym użyciem FragmentManager. Główna zasada: wykonuj transakcje przez commit() (asynchronicznie) lub commitNow() (synchronicznie) w zależności od kontekstu. commit() gwarantuje prawidłową kolejność przy wielokrotnych transakcjach.

Zapisywanie stanu Fragment: onSaveInstanceState

Fragment obsługuje własny mechanizm zapisywania stanu przez onSaveInstanceState, który działa niezależnie od Activity. Fragment zapisuje stan w Bundle, który jest przekazywany do onCreate i onCreateView przy przywracaniu.

Kiedy Fragment zapisuje stan:

  • Przy obrocie ekranu — View jest niszczony, Fragment zapisuje stan w Bundle.
  • Przy dołączeniu Fragment do Activity po process death.
  • Przy wywołaniu onSaveInstanceState z Activity (system rozprzestrzenia zapisywanie na wszystkie dziecięce fragmenty).

Nowoczesne podejście: używaj SavedStateHandle w ViewModel do zapisywania stanu Fragment. SavedStateHandle automatycznie zapisuje i przywraca dane przy obrocie ekranu i process death, nie wymagając ręcznego onSaveInstanceState. Google zaleca SavedStateHandle jako preferowany sposób zapisywania stanu UI w Fragment.

setRetainInstance (przestarzałe od Fragment 1.3): wcześniej Fragment mógł być zachowywany przez setRetainInstance(true) przy obrocie ekranu. To podejście zostało zastąpione przez ViewModel + SavedStateHandle, które działają niezawodniej i nie wymagają specjalnej konfiguracji.

viewLifecycleOwner: oddzielny cykl życia View

viewLifecycleOwner — Lifecycle powiązany z View Fragment (od onCreateView do onDestroyView). To koncepcyjnie ważne: subskrypcje LiveData/Flow wykonane przez viewLifecycleOwner są automatycznie anulowane przy zniszczeniu View (onDestroyView), ale nie wpływają na sam Fragment.

Różnica między viewLifecycleOwner a lifecycle Fragment:

  • lifecycle (Fragment) — żyje od onAttach do onDetach. Subskrypcje pozostają aktywne nawet po zniszczeniu View.
  • viewLifecycleOwner — żyje od onCreateView do onDestroyView. Subskrypcje są anulowane przy zniszczeniu View.

Dlaczego to ważne: jeśli subskrybujesz LiveData przez lifecycle Fragment (this), to po onDestroyView subskrypcja pozostaje aktywna i LiveData będzie próbować aktualizować null View, powodując NPE. Subskrypcja przez viewLifecycleOwner gwarantuje, że po onDestroyView żadne aktualizacje UI nie nastąpią.

Zasada: w Fragment zawsze używaj viewLifecycleOwner do subskrypcji LiveData, Flow i korutyn związanych z UI. Do korutyn ViewModel używaj viewModelScope — jest on powiązany z ViewModel, a nie z Fragment.

Przykłady kodu Fragment w Kotlin

Przykład 1: Podstawowy Fragment z onViewCreated i viewLifecycleOwner

Demonstruje prawidłową inicjalizację UI i subskrypcję LiveData przez viewLifecycleOwner.

kotlin
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", "Aktualizacja listy: ${users.size} użytkowników")
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d("UserListFragment", "onDestroyView: View zniszczony")
    }
}

Fragment nadmuchuje layout w onCreateView, konfiguruje UI i subskrybuje LiveData w onViewCreated. Subskrypcja przez viewLifecycleOwner — obowiązkowy wymóg zapobiegania wyciekom. onDestroyView loguje zniszczenie View — potwierdzenie, że Fragment przetrwa obrót ekranu.

Przykład 2: Fragment z FragmentManager i transakcjami

Demonstruje dodawanie Fragment przez FragmentManager w Activity, zastępowanie z BackStack i przywracanie.

kotlin
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", "Ładowanie szczegółów użytkownika: $userId")
    }
}

Activity używa supportFragmentManager do zarządzania fragmentami. Transakcja add() z BackStack gwarantuje, że po naciśnięciu „Wstecz" HomeFragment zostanie przywrócony. openDetail() zastępuje bieżący Fragment na DetailFragment z argumentami. Sprawdzenie savedInstanceState == null zapobiega duplikowaniu fragmentów przy obrocie ekranu.

Przykład 3: Fragment z LifecycleObserver i StateFlow

Użycie Flow i StateFlow w Fragment z viewLifecycleOwner do reaktywnego aktualizowania UI.

kotlin
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", "Wyniki wyszukiwania: ${results.size}")
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
    }
}

Fragment używa View Binding do dostępu do View. Korutyna viewLifecycleOwner.lifecycleScope.launch jest automatycznie anulowana przy zniszczeniu View. Binding jest zerowany w onDestroyView w celu zapobiegania wyciekom. StateFlow gwarantuje aktualność danych przy odtworzeniu View.

Często zadawane pytania

Czym się różni onViewCreated od onCreateView?

onCreateView — tworzy i zwraca główny View Fragment. onViewCreated — wywoływany zaraz po utworzeniu View, gwarantuje, że View jest w pełni zainicjalizowany i gotowy do konfiguracji (findViewById, subskrypcje). Google zaleca w onCreateView tylko nadmuchiwać layout, a całą konfigurację UI robić w onViewCreated.

Kiedy Fragment jest faktycznie niszczony — onDestroy czy onDetach?

onDestroy — Fragment jest zniszczony jako obiekt (ViewModel jest czyszczony, korutyny anulowane). onDetach — ostatni callback, po którym Fragment odłącza się od Activity. Praktycznie wszystkie zasoby powinny być zwolnione w onDestroyView (View) i onDestroy (Fragment). onDetach — do czyszczenia referencji do Activity.

Dlaczego Fragment znika po obrocie ekranu?

Fragment znika, jeśli nie został dodany do FragmentManager przez transakcję z zachowaniem w BackStack lub jeśli Activity nie przywraca FragmentManager w onCreate. Rozwiązanie: dodawaj Fragment programowo przez supportFragmentManager.beginTransaction().add() w onCreate z sprawdzeniem savedInstanceState == null.

Czy Fragment może istnieć bez Activity?

Nie. Fragment jest zawsze powiązany z Activity przez FragmentManager. Nawet przy obrocie ekranu Activity jest odtwarzane, a Fragment ponownie przywiązywany do nowego Activity. Utworzenie Fragment poza Activity jest niemożliwe — konstruktor Fragment wymaga pustego konstruktora do przywrócenia przez system.

Czym są nested fragments i do czego służą?

Nested fragments (zagnieżdżone fragmenty) — Fragment wewnątrz innego Fragment. Używane do budowania złożonych ekranów: panele z zakładkami, master-detail. Zagnieżdżone fragmenty są zarządzane przez dziecięcy FragmentManager (childFragmentManager). Google zaleca nie przekraczać 2 poziomów zagnieżdżenia, aby uniknąć problemów z wydajnością.

Podsumowanie

  • Fragment Lifecycle obejmuje 11 callbacków: onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume → onPause → onStop → onDestroyView → onDestroy → onDetach.
  • FragmentManager zarządza stanami Fragment (INITIALIZED → CREATED → STARTED → RESUMED → DESTROYED) i BackStack transakcji.
  • Fragment przetrwa obrót ekranu — View jest niszczony (onDestroyView), ale Fragment i ViewModel pozostają żywe.
  • viewLifecycleOwner — oddzielny Lifecycle dla View Fragment; obowiązkowy do subskrypcji LiveData i korutyn UI.
  • Zapisywanie stanu Fragment — przez onSaveInstanceState lub SavedStateHandle w ViewModel.
  • Transakcje Fragment są wykonywane przez FragmentManager z commit() (asynchronicznie) lub commitNow() (synchronicznie).
  • Zawsze zeruj binding i referencje do View w onDestroyView, aby zapobiec wyciekom pamięci.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również