728x90


📌  생명주기란?

Lifecycle은 Life + cycle의 합성어이다. 말 그대로 앱이 탄생하고 진행되며 죽음(?)에 이르기까지의 과정이라고 생각하면 된다. Activity, Fragment, Service 총 세 가지 종류의 Lifecycle이 존재한다.

 

지난번 Acticity 생명주기에 이어 이번에는 Fragment 생명주기에 대해 다뤄보려고 한다.

 

 

📌 프래그먼트 특징

프래그먼트 뷰는 프래그먼트 생명주기와 독립적으로 관리되는 별도의 생명주기가 있다. 프래그먼트가 다른 생명주기로 전환되려면 FragmentManager가 있어야 한다. 

 

여기서 FragmentManager란?

 

공식문서

FragmentManager is the class responsible for performing actions on your app's fragments, such as adding, removing, or replacing them, and adding them to the back stack.

 

즉 FragmentManger 란 앱의 프래그먼트를 더하고, 삭제하고, 교체하고, 백스택에 더하는 활동 등을 책임을 지는 클래스이다. 또한, 생명 주기 외에도 호스트 액티비티에 프래그먼트를 연결하고, 프래그먼트가 더 이상 사용되지 않으면 분리하는 역할도 한다.

 

여기서 주의할 점은 프래그먼트 생명주기는 호스트 액티비티나 호스트 프래그먼트 생명주기보다 앞설 수 없다. 예를 들어 호스트 액티비티나 프래그먼트는 자식 프래그먼트보다 먼저 실행되어야 하고, 자식 프래그먼트는 호스트 액티비티나 호스트 프래그먼트보다 먼저 정지되어야 한다.

 

위와 같은 상황을 방지하기 위해 XML 파일에 <fragment> 태그로 프래그먼트 대신 <FragmentContainerView> 를 추가하면 된다.

 

 Fragment 생명주기 그래프

 

 

위 그림은 FragmentManager에 프래그먼트가 추가된 이후 생명주기를 나타낸다. 즉 onAttach() 함수가 호출된 이후이다.

 

onAttach()

onAttach()는 프래그먼트가 FragementManager에 추가되고, 호스트 액티비티에 연결될 때 호출된다. 이 시점에 프래그먼트가 활성화되고, FragmentManager는 프래그먼트의 생명주기를 관리한다.

 

onCreate()

Fragment 만 CREATED 가 된 상황이다.

이는 FragmentManager 에 add 됐을 때 도달하며 onCreate() 콜백 함수를 호출한다. 주의할 점은 onCreate() 이전에 onAttach()가 먼저 호출된다는 것이다.

 

중요한 점은 이 시점에는 아직 Fragment View가 생성되지 않았기 때문에 Fragment의 View와 관련된 작업을 수행하지 않도록 해야한다. Fragment를 생성하면서 넘겨준 값들이 있다면, 여기서 변수에 넣어주면 된다.

 

🚫 onAcitivityCreated 지원 중단

2020년 3월 18일 업데이트 이후 Fragment의 onActivityCreated가 deprecated 되면서 Activity의 생명주기와 Fragment의 생명주기가 서로 영향을 덜 주는 방향으로 개편되었다. 원래 onActivityCreated()의 존재 이유 자체가 Activity의 생성이 끝나야만 처리되어야 하는 로직이 Fragment에 있을 때를 위한 것이다. 실제 프로젝트를 수행하다 보면 빈번히 발생하는 일이기도 하다.

기존에는 onActivityCreated 에서 Fragment 관련 초기화 루틴을 거의 수행했었는데 onActivityCreated 가 사라지면서 공식문서에서는 

다음과 같이 View 관련 초기화는 onViewCreated, 나머지 초기화 루틴은 onCreate에서 수행하라고 나와있다.

 

 

onCreateView()

Fragment가 View를 그리기 위한, 즉 Layout을 Inflate 하는 작업을 수행하는 부분이다. 따라서 반환 값도 View가 된다. 다만 UI를 아직 그리지 않았다면 null을 반환해도 된다.

 

 

onViewCreated()

onCreateView() 를 통해 반환된 View 객체는 onViewCreated()의 파라미터로 전달되는데, 이 시점부터는 Fragment View의 Lifecycle 이 INITIALIZED 상태로 업데이트됐기 때문에 View 의 초기값을 설정해주거나 LiveData Observing, RecyclerView나 ViewPager2에 사용할 Adapter 초기화 등을 이곳에서 수행하는 것이 적절하다.

 

 

 

onViewStateRestored()

onViewStateRestored() 함수는 저장해둔 모든 state 값이 Fragment의 View 계층구조에 복원됐을 때 호출된다. View lifecycle owner 는 이때 INITIALIZED 상태에서 CREATED 상태로 변경됐음을 알린다.

 

 

onStart()

Fragment 가 사용자에게 보여질 수 있을 때 호출된다. 이 시점부터는 Fragment의 child FragmentManager 통해 FragmentTransaction 을 안전하게 수행할 수 있다.

 

 

onResume()

Fragment 가 보이는 상태에서 모든 Animator 와 Transition 효과가 종료되고, 프래그먼트가 사용자와 상호작용할 수 있을 때 onResume() 콜백이 호출된다.

Resumed 상태가 됐다는 것은 사용자가 프래그먼트와 상호작용 하기에 적절한 상태가 된 것이다.

 

 

onPause()

사용자가 Fragment를 떠나기 시작했지만 Fragment는 여전히 visible 일 때 onPause()가 호출된다.

 

 

onStop()

Fragment 가 더 이상 화면에 보이지 않게 되면 Fragment와 View의 Lifecycle 은 CREATED 상태가 되고, onStop() 콜백 함수가 호출되게 된다. 

onStop()이 onSaveInstanceState() 함수보다 먼저 호출됨으로써 onStop()이 FragmentTransaction을 안전하게 수행할 수 있는 마지막 지점이 된다.

 

 

onDestroyView()

모든 exit animation과 transition 이 완료되고, Fragment 가 화면으로부터 벗어났을 경우 Fragment View의 Lifecycle 은 DESTROYED 가 되고 onDestroy()가 호출된다.

그리고 해당 시점에서는 가비지 컬렉터에 의해 수거될 수 있도록 Fragment View에 대한 모든 참조가 제거되어야 한다.

 

 

onDestroy()

Fragment 가 제거되거나 FragmentManager 가 destroy 됐을 경우, 프래그먼트의 Lifecycle 은 DESTROYED 상태가 되고, onDestroy() 콜백 함수가 호출된다. 해당 지점은 Fragment Lifecycle의 끝을 알린다.

그리고 onAttach()가 onCreate() 이전에 호출됐던 것처럼 onDetach() 또한 onDestroy() 이후에 호출되게 된다.

 

 

 

동작예시

액티비티의 이동에서의 생명주기는 많이 찾아볼 수 있지만 프래그먼트의 이동에서 생명주기 호출은 찾아보기 힘들어 몇 가지 동작을 실제 로그를 찍어 확인해보았다.

 

아래 예시는 Jetpack Navigation을 사용해 프래그먼트의 이동을 구현한 예시이다. 만약 FragmentManager로 프래그먼트의 이동을 구현했다면 구현 로직에 따라 아래 결과와 다를 수 있다.

 

프래그먼트 A 생성

프래그먼트 A 생성

  • 위에서 보았던 프래그먼트 생명주기 그래프대로 호출되는 것을 확인할 수 있다.

 

 

프래그먼트 A에서 프래그먼트 B로 이동

프래그먼트 A에서 프래그먼트 B로 이동

  • 우선 프래그먼트 A의 onPause(), onStop()이 호출된다.
  • 그 이후 프래그먼트 B가 일반적인 프래그먼트 생성 순서대로 생명주기 함수를 호출한다. 
  • 프래그먼트 B가 사용자와 상호작용 준비를 마치면, 즉 onResume()함수가 호출되면 프래그먼트 A의 onDestroyView()가 호출된다.

액티비티에서는 액티비티 A에서 액티비티 B로 이동할 때 onPause까지만 호출하고 액티비티 B가 상호작용 준비를 마치면 onStop()을 호출했던 것과 다르게 프래그먼트는 프래그먼트 B의 onAttach()가 호출되기도 전에 onStop()까지 호출되는 것을 확인할 수 있다.

 

 

프래그먼트 B에서 백버튼을 눌러 다시 프래그먼트 A로 이동

프래그먼트 B에서 백버튼을 눌러 다시 프래그먼트 A로 이동

  • 프래그먼트 B가 onPause(), onStop()을 차례로 호출한다.
  • 프래그먼트 A는 onAttach(), onCreate()는 다시 호출되지 않고 onCreateView()부터 다시 호출되는 것을 확인할 수 있다.
  • 프래그먼트 A가 onResume()을 호출하여 사용자와 상호작용 준비를 마치면 프래그먼트 B는 onDestroyView(), onDestroy(), onDetach()를 차례로 호출하며 백스택에서 자신을 제거한다.

 

추가로 그래프 그림에 있는 onSaveInstanceState() 함수는 그 어디에도 보이지 않는데 이는 onSaveInstanceState() 함수는 생명주기 콜백함수가 아니라 화면 회전과 같은 Configuration Change가 발생했을 때 데이터를 보존하기 위해 사용하는 함수이기 때문이다. 

 

 

 

 

 

 

복사했습니다!