본문 바로가기

안드로이드

[Android] Fragment Lifecycle

 

FragmentLifecycle에 대해 알아보자.

 

안드로이드 개발을 하면서 수많은 Fragment들을 사용하게 된다. 

 

Fragment들은 모두 각각의 lifecycle을 가지고 있다.

 

Fragment 클래스를 보면

LifecyclerOwner 인터페이스를 구현하는 것을 알 수 있다.

 

public interface LifecycleOwner {
    // return the lifecycle of the provider.
    @NonNull
    Lifecycle getLifecycle();
}

 

 

Fragment에서는 getLifecycle 메서드를 통해 Lifecycle을 알 수 있다.

 

Lifecycle의 상태는 아래와 같다.

 

public enum State {
    // Destroyed state for a LifecycleOwner.
    // After this event, this Lifecycle will not dispatch
    // any more events. For instance, this state is reached right before 
    // Activity's onDestroy call.
    DESTROYED,
    // Initialized state for a LifecycleOwner. This is the state
    // when it is constructed but has not received onCreate yet.
    INITIALIZED,
    // Created state for a LifecycleOwner. This state is reached in 
    // 1. after onCreate call
    // 2. right before onStop call
    CREATED,
    // Started state for a LifecycleOwner. This state is reached in
    // 1. after onStart call
    // 2. right before onPause call
    STARTED,
    // Resumed state for a LifecycleOwner
    // This state is reached after onResume is called
    RESUMED;
}

 

 

이제 안드로이드 공식 문서에서 소개하고 있는 lifecycle의 상태 그림을 보도록 하자.

 

fragment lifecycle

 

위 그림은 Fragment 생명주기의 상태와 Fragment callback 메소드와 Fragment View의 생명주기와의 관련성을 보여주고 있다.

 

그림을 보면 Fragment는 생명주기를 도는 동안 state를 통해 이동합니다. 


문서에서 보여주는 예시로,

back stack의 맨 위에 추가된 프래그먼트는

CREATED -> STARTED -> RESUMED 순서로 위쪽으로 이동한다.


반대로 프래그먼트가 back stack에서 없어지면

RESUMED -> STARTED -> CREATED -> DESTROYED로 아래로 이동한다.

 

 

Fragment Lifecycle, Callback, View Lifecycle의 관계를 보면서 이해해 보도록 하자.

 

 

 

onAttach()


Fragment가 Host Activity에 attach 된다.

 

onAttach 메서드는 onCreate 메서드 이전에 호출되는 것을 기억하자.

 

override fun onAttach(context: Context) {
    super.onAttach(context)
}

 

 

onCreate()


Fragment가 CREATED만 된 상태

 

Fragment가 CREATED 상태가 되면 FragmentManager에 추가된다.

 

// Fragment 자체가 생성
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
}

 

 

onCreate 메서드는 onSaveInstanceState에 의해 저장된 모든 상태를 갖는 Bundle 타입인 savedInstanceState를 가진다.

savedInstanceState는 fragment가 처음 생성될 때 null 값을 가지고, 재생성시에는 항상 non-null 이다.

 

여기는 Fragment의 SavedStateRegistry를 통해 fragment와 관련된 저장된 상태를 복원하기에 알맞은 곳이다.

 

이때 주의할 점은, View의 Lifecycle은 아직 초기화되지 않아서 view와 관련된 작업은 onCreate()에서 하지 않아야 한다.

 

 

 

onCreateView(), onViewCreated()


Fragment CREATED & View INITIALIZED

 

Fragment를 사용하면서 가장 많이 사용되는 callback 함수이기도 하다.

 

// Fragment view 초기화, 정상 초기화가 되었다면 view 반환.
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return super.onCreateView(inflater, container, savedInstanceState)
}

// onCreateView에서 view 객체가 반환되고 호출된다. view 생성 보장
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}

 

Fragment view의 생명주기는 fragment가 onCreateView를 통해 유효한 view의 인스턴스를 제공할 때 생성된다.

 

@LayoutId를 가진 fragment 생성자를 이용해 적절하게 view를 자동으로 inflate 하거나, onCreateView 메소드를 재정의해 view를 inflate하거나 생성한다.

 

View가 non-null로 인스턴스화될 때
해당 view가 fragment에 설정되고 getView 메서드를 통해 알 수 있다.

 

// @return The fragment's root view, or null if it has no layout.
@Nullable
public View getView() {
    return mView;
}

 


이후 getViewLifecycleOwnerLiveData 메서드가 fragment view에
해당하는 새로 initialized 된 LifecycleOwner로 업데이트된다.

 

// @return A LiveData that changes in sync with getViewLifecycleOwner()
@NonNull
public LiveData<LifecycleOwner> getViewLifecycleOwnerLiveData() {
    return mViewLifecycleOwnerLiveData;
}

 


이때 onViewCreated() callback이 호출된다.

 

View가 정상적으로 반환되면 onViewCreated()의 파라미터로 전달되고 이때부터는 View의 lifecycle이 INITIALIZED 상태로 업데이트되어 View 초기값 설정, LiveData observing, Adapter 설정 등의 작업을 진행하면 된다.

 

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}

 

 

 

onViewStateRestored()


Fragment & View CREATED

 

onViewStateRestored() callback은 이전에 저장했던 state 값이 Fragment View 계층에 복원되었을 때 호출된다.

여기서는 각 view의 상태값을 체크할 수 있다

 

override fun onViewStateRestored(savedInstanceState: Bundle?) {
    super.onViewStateRestored(savedInstanceState)
}

 

 

 

onStart()


Fragment & View STARTED

 

유저에게 Fragment가 보일 때 onStart()가 호출된다.

이 fragment가 붙어있는 Activity의 onStart 콜백 시점과 유사하다.

이 때는 childFragmentManager를 통해 FragmentTransaction을 안전하게 수행할 수 있다.

 

override fun onStart() {
    super.onStart()
}

 

 

 

onResume()


Fragment & View RESUMED

 

Fragment가 보이는 상태로 모든 Animator, Transition이 종료된 후, Fragment가 유저와 상호작용 할 수 있을 때 onResume() 이 호출된다.

onStart()와 마찬가지로 Activity의 onResume() 시점과 유사하다.

onResume이 호출되지 않았을 때는 view에 포커스를 설정하거나 input method visibility를 시도하는 등의 작업은 하면 안 된다.

 

override fun onResume() {
    super.onResume()
}

 

 

 

onPause()


Fragment & View STARTED

 

유저가 fragment를 떠나기 시작하고 조금이라도 fragment가 보일 때(visible) 호출된다.

 

override fun onPause() {
    super.onPause()
}

 

 

 

onStop(), onSaveInstanceState()


Fragment & View CREATED

 

Fragment가 더 이상 화면에 보이지 않을 때 호출된다.

Fragment와 View의 Lifecycle은 CREATED 상태가 된다.

Host Activity, Fragment가 중단되거나 이들의 상태가 저장될 때도 호출된다.

 

override fun onStop() {
    super.onStop()
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
}

 

 

여기서 주의할 점은, API 28 버전을 기준으로 onSaveInstanceState() callback 함수와 onStop 함수의 호출 순서가 달라졌다.

API 28 이후부터 onStop 함수가 FragmentTransaction을 안전하게 수행할 수 있는 마지막 지점이 되었다.

onStop(), onSaveInstanceState() 순서

 

 

 

onDestroyView()


Fragment CREATED, View DESTROYED

 

모든 exit animation, transition이 완료.

Fragment가 화면에서 벗어난 경우 View의 lifecycle은 Destroyed가 되며 onDestroyView()가 호출된다.

이때부터는 getViewLifecycleOwnerLiveData 메서드가 null을 리턴한다.

 

하지만 Fragment는 아직 메모리에 남아있어서 뷰에 대한 모든 참조를 제거해서 GC에 의해 수거될 수 있도록 해야 메모리 누수가 생기지 않는다.

 

// Fragment View 소멸
override fun onDestroyView() {
    super.onDestroyView()
}

 

 

onDestroy()


Fragment DESTROYED

 

Fragment 나 FragmentManager가 destroy 된 경우 Fragment lifecycle은 Destroyed가 되며 callback으로 onDestroy()가 호출된다.

Fragment의 lifecycle도 종료된다.

 

// Fragment 자체가 소멸
override fun onDestroy() {
    super.onDestroy()
}

 

 

 

onDetach()


onDestroy() 이후에 호출된다.

 

override fun onDetach() {
    super.onDetach()
}