각 프래그먼트 인스턴스는 자기 자신의 생명주기를 가집니다. 사용자가 앱을 탐색하고 상호 작용할 때 프래그먼트는 추가, 제거 및 화면에 들어가거나 나올 때 그들의 생명주기 내에서 다양한 상태로 전환됩니다.
생명주기를 관리하기 위해서, 프래그먼트는 LifecycleOwner
라는 인터페이스를 구현하고 있으며, 개발자는 getLifecycle()
메서드로 Lifecycle
객체에 접근할 수 있습니다.
각각의 생명주기 상태는 Lifecycle.State
라는 enum 클래스로 표현됩니다.
- INITIALIZED
- CREATED
- STARTED
- RESUMED
- DESTROYED
Lifecycle 위에 프래그먼트를 생성하면, Lifecycle-Aware 컴포넌트와 함께 생명주기를 다루는 데 사용되는 클래스들을 사용할 수 있게 됩니다. Lifecycle-Aware 컴포넌트는 프래그먼트의 활성, 비활성 상태를 자동으로 인식할 수 있습니다.
LifecycleObserver를 사용하는 대신에, 프래그먼트 클래스는 프래그먼트의 생명주기 변화에 상응하는 콜백 함수들을 포함하고 있습니다. onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()가 이에 해당됩니다.
프래그먼트의 뷰는 프래그먼트와 독립된 별도의 생명주기를 갖습니다. 프래그먼트는 뷰에 대한 LifecycleOwner를 유지하며, 이는 getViewLifecycleOwner() 또는 getViewLifecycleOwnerLiveData()로 접근할 수 있습니다.
프래그먼트 뷰에 대한 생명주기에 접근하는 것은, Lifecycle-Aware 컴포넌트가 프래그먼트의 뷰가 존재하는 동안에만 작업을 수행해야 하는 상황 (예: 화면에만 표시되는 LiveData 관찰)에 유용합니다.
이 포스팅에서는 프래그먼트 생명 주기에 대해 자세히 설명하고, 프래그먼트의 생명 주기 상태를 결정하는 몇 가지 규칙을 설명할 것입니다. 그리고 생명 주기 상태와 프래그먼트 생명 주기 콜백 간의 관계도 알아볼 것입니다.
프래그먼트가 초기화 되면, INITIALIZED
상태로 시작합니다. 프래그먼트 생명주기 내에서 다른 상태로 전환하기 위해, 프래그먼트는 반드시 FragmentManager에 추가되어야 합니다. FragmentManager는 프래그먼트의 상태를 결정하고 그 상태로 이동시킵니다.
프래그먼트 생명주기를 넘어서, FragmentManager는 프래그먼트를 호스트 액티비티에 연결하고 프래그먼트가 더 이상 사용되지 않을 때 분리하는 역할도 수행합니다. 이를 위해 프래그먼트 클래스는 onAttach()
, onDetach()
라는 콜백 함수를 갖고 있으며, 개발자는 이러한 이벤트 중에 하나가 발생할 때 수행할 작업을 재정의할 수 있습니다.
onAttach()
콜백 함수는 프래그먼트가 FragmentManager에 추가되어 호스트 액티비티에 부착될 때 호출됩니다. 이 지점에서 프래그먼트가 활성화 되며, FragmentManager는 프래그먼트의 생명주기 상태를 관리하기 시작합니다. 그리고 findFragmentById()
와 같은 FragmentManager 함수들로 프래그먼트 객체를 획득할 수 있습니다.
onAttach()
함수는 항상 생명주기 상태 변화가 있기 전에 호출됩니다.
onDetach()
콜백 함수는 프래그먼트가 FragmentManager로부터 삭제되어 호스트 액티비티로부터 분리될 때 호출됩니다. 프래그먼트는 비활성 상태로 전환되며, findFragmentById()
함수로 프래그먼트를 획득할 수 없습니다.
onDetach()
함수는 항상 생명주기 상태 변화 이후에 호출됩니다.
참고로, 이러한 콜백 함수들은 FragmentTransaction의 attach(), detach() 함수와는 무관합니다.
주의: FragmentManager에서 제거된 Fragment 인스턴스를 재사용하지 마십시오. 프래그먼트가 자신의 내부 상태를 정리하는 동안 실수로 자신의 상태를 재사용된 인스턴스로 전달할 수 있습니다.
프래그먼트의 생명주기 상태를 결정할 때, FragmentManager는 다음과 같은 것을 고려합니다.
주의: <fragment> 태그를 사용하면 프래그먼트가 해당 FragmentManager의 상태를 넘어서 이동할 수 있으므로, XML에서 프래그먼트를 추가할 때 <fragment> 태그를 사용하지 마십시오. 대신 XML을 사용하여 프래그먼트를 추가하려면 항상 FragmentContainerView를 사용하십시오.
프래그먼트의 생명 주기가 진행됨에 따라 프래그먼트의 상태는 위아래로 이동합니다. 예를 들어, 백 스택의 맨 위에 push 된 프래그먼트는 CREATED → STARTED → RESUMED 처럼 위쪽으로 이동합니다. 반대로, 프래그먼트가 백 스택에서 pop 되면 RESUMED → STARTED → CREATED → DESTROYED 처럼 아래쪽으로 이동합니다.
Fragment CREATED
프래그먼트가 CREATED
상태에 도달하면, 프래그먼트는 FragmentManager에 추가되어 onAttach() 함수가 이미 호출된 상태입니다.
이 위치는 프래그먼트의 SavedStateRegistry를 통해 '프래그먼트 자체'와 관련된 모든 저장된 상태를 복원하기에 적절합니다. 현재 프래그먼트의 뷰가 생성되지 않았으므로 '프래그먼트의 뷰'와 관련된 모든 상태는 뷰가 생성된 후에만 복원해야 합니다.
이러한 상태 변화는 onCreate()
콜백을 호출합니다. 이 콜백에서 onSaveInstanceState()에 의해 저장된 이전 상태를 포함하는 savedInstanceState
번들 인자를 받습니다. 프래그먼트가 처음 생성될 때 savedInstanceState
는 null이지만, 다음에 재생성될 때는 onSaveInstanceState()를 오버라이드 하지 않아도 항상 null이 아닙니다.
Fragment CREATED and View INITIALIZED
프래그먼트 뷰의 생명주기는 프래그먼트가 유효한 뷰 인스턴스를 제공할 때만 생성됩니다.
대부분의 경우, @LayoutId를 갖는 프래그먼트 생성자를 사용하여 onCreateView()를 재정의하지 않아도 적절한 시점에 자동으로 뷰를 인플레이트 할 수 있습니다. 또한 onCreateView()를 재정의하여 프래그먼트의 뷰를 프로그래밍 방식으로 인플레이트 하거나 생성할 수도 있습니다.
프래그먼트의 뷰가 null이 아닌 뷰로 인스턴스화 된 경우에만, 해당 뷰가 프래그먼트에 설정되며 getView()
로 객체를 획득할 수 있습니다. 그런 다음 getViewLifecycleOwnerLiveData()
가 프래그먼트의 뷰에 해당하는 새로 초기화된 LifecycleOwner
로 업데이트 됩니다.
이때 onCreateView()에서 반환된 뷰 객체는 onViewCreated()
콜백 함수의 매개변수로 전달됩니다. onViewCreated()는 뷰의 초기 상태를 설정하고, 프래그먼트의 뷰를 업데이트하는 LiveData 인스턴스를 관찰하고, 프래그먼트의 뷰에 있는 RecyclerView 또는 ViewPager2 인스턴스에 어댑터를 설정하기에 적절한 시점입니다.
Fragment and View CREATED
프래그먼트의 뷰가 생성되면 이전 뷰 상태가 있는 경우 복원되고, 뷰의 생명주기가 CREATED
상태로 이동합니다. 또한 뷰의 LifecyclerOwner는 옵저버에게 ON_CREATE
이벤트를 발생시킵니다. 여기에서 프래그먼트의 뷰와 관련된 모든 추가적인 상태를 복원해야 합니다.
이 전환은 또한 onViewStateRestored()
콜백을 호출합니다.
Fragment and View STARTED
여기에서 Lifecycle-aware 컴포넌트를 프래그먼트의 STARTED
상태에 연결하는 것을 강력히 권장합니다. 왜냐하면, 이 시점은 프래그먼트 뷰가 생성된 경우 이용 가능하며, 프래그먼트의 하위 FragmentManager에서 FragmentTransaction을 수행하는 것이 안전하다고 보장되기 때문입니다. 프래그먼트 뷰가 null이 아니라면, 프래그먼트 뷰의 생명주기는 프래그먼트 생명주기가 STARTED
상태로 이동하자마자 STARTED
상태로 이동합니다.
프래그먼트가 STARTED
상태가 되면, onStart()
콜백 함수가 호출됩니다.
Fragment and View RESUMED
프래그먼트가 표시되면, 모든 애니메이터 및 트랜지션 효과가 완료된 것이며, 프래그먼트는 사용자 상호 작용하기 위한 준비가 완료된 것입니다. 이때 프래그먼트의 생명주기가 RESUMED
상태로 이동하고 onResume()
콜백이 호출됩니다.
RESUMED
상태로의 전환은 사용자가 이제 프래그먼트와 상호 작용할 수 있음을 나타내는 적절한 신호입니다. RESUMED
상태가 아닌 프래그먼트는, 뷰에 수동으로 포커스를 설정하거나 입력 함수의 visibility을 조절하려고 시도하면 안 됩니다.
Fragment and View STARTED
사용자가 프래그먼트를 떠나기 시작했지만 프래그먼트가 계속 표시되는 경우, 프래그먼트와 해당 뷰의 생명주기는 STARTED
상태로 돌아가며 해당 옵저버에게 ON_PAUSE
이벤트를 발생시킵니다. 프래그먼트는 이후 onPause()
콜백을 호출합니다.
Fragment and View CREATED
프래그먼트가 더 이상 표시되지 않으면 프래그먼트와 해당 뷰에 대한 생명주기가 CREATED
상태로 이동하고, 해당 옵저버에게 ON_STOP
이벤트를 발생시킵니다.
이 상태 전환은 상위 액티비티 또는 프래그먼트가 중지될 때 뿐만 아니라 상위 액티비티 또는 프래그먼트의 상태 저장에 의해서도 트리거 됩니다. 이 동작은 프래그먼트의 상태가 저장되기 '전에' ON_STOP
이벤트가 호출되도록 보장합니다. 따라서 ON_STOP
이벤트는 자식 FragmentManager에서 FragmentTransaction을 수행해도 안전한 마지막 시점이 됩니다.
위의 그림에서 볼 수 있듯이, onStop()
콜백 함수와 상태를 저장하는 onSaveInstanceState()
함수의 호출 순서는 API 레벨에 따라 다릅니다. API 28 이전의 모든 API 레벨의 경우, onStop()
전에 onSaveInstanceState()
가 호출됩니다. API 레벨 28 이상에서는 호출 순서가 뒤바뀝니다.
Fragment CREATED and View DESTROYED
모든 종료 애니메이션과 트랜지션이 완료되고 프래그먼트의 뷰가 윈도우에서 분리되면, 프래그먼트 뷰의 생명주기가 DESTORYED
상태로 이동하고 해당 옵저버에게 ON_DESTROY
이벤트를 발생시킵니다. 그런 다음 프래그먼트는 onDestroyView()
콜백을 호출합니다. 이 시점에서 프래그먼트의 뷰는 생명주기가 끝나게 되고, getViewLifecycleOwnerLiveData()
는 null을 반환합니다.
이 시점에서 프래그먼트의 뷰에 대한 모든 참조가 제거되어 프래그먼트의 뷰가 가비지 컬렉터에 의해 수집될 수 있어야 합니다.
Fragment DESTROYED
프래그먼트가 제거되거나 FragmentManager가 소멸되면, 프래그먼트의 생명주기가 DESTROYED
상태로 이동하고 해당 옵저버에게 ON_DESTRY
이벤트를 발생시킵니다. 그런 다음 프래그먼트는 onDestroy()
콜백을 호출합니다. 이 시점에서 프래그먼트는 생명주기의 끝에 도달한 것입니다.