우당탕탕 Android
[Android] Fragment, Fragment Lifecycle에 대해 알아보자! 본문
이 글에서는 Fragment와 Fragment의 Lifecycle에 대해 알아본다.
들어가기 전
공부하는 것을 정리하고, 나중에 기억나지 않을 때 참고하기 위한 용도의 글이므로 100% 정확하지 않을 수 있습니다! 제가 이해한 것은 모두 적은 글이니 틀린 부분이 있다면 이야기해 주세요 (ง •̀ω•́)ง✧
# 공부하게 된 계기
이전 프로젝트에서 협업하던 팀원이 코드를 생명주기에 맞게 깔끔하게 작성하고자 했다. 두 사람의 지식 차이로 인해 일정 기간 동안 공부를 진행한 후, 서로에게 지식을 전달하기 위해 시간을 가지기로 했다. 그래서 Activity와 Fragment에 대해 다시 공부하게 되었고, 학습한 내용을 기록하고 공유하기 위해 티스토리에 글을 작성하게 되었다.
Fragment란?
기존에 스마트폰 화면이 작았기 때문에 Activity 만을 이용하여 화면을 구성해도 아무런 문제가 없었다. 그러나 태블릿 PC의 등장으로 화면이 넓어지며, Activity 하나로 UI, 사용자 이벤트를 처리하기에는 복잡해지고 코드가
기존에 스마트폰 화면이 작았기 때문에 Activity 만을 이용하여 화면을 구성해도 아무런 문제가 없었다. 그러나 태블릿 PC의 등장으로 화면이 넓어지며, Activity 하나로 UI, 사용자 이벤트를 처리하기에는 복잡해지고 코드가 길어졌다. 그래서 처음으로 나온 대안이 View 클래스로 Activity의 역할을 대신하는 것이었지만, View는 오로지 화면 출력만을 위해 설계된 클래스이기 때문에 Activity의 생명주기를 이용할 수 없어 모든 역할을 대신할 수 없었다. View 클래스 중 Activity와 생명주기가 같은 클래스가 있다면 Activity의 코드 중 일부를 분리해서 개발할 수 있게 될 것이라는 아이디어가 나왔고, 그렇게 구현된 클래스가 바로 Fragment이다.
Fragment는 Activity처럼 이용할 수 있는 View이며 Activity 위에서 화면을 유동적으로 구사해주는 안드로이드 기법이다.
Fragment는 Activity나 다른 Fragment에 의해 호스팅되어야 하며, 독립적으로 화면 구성이 가능하고 자체적인 입력 이벤트를 처리할 수 있다. Activity에 속해 있는 Button들을 통하는 방법 등으로 Fragment를 보이게 하거나 안 보이게 할 수 있으며 다른 Fragment로 바꾸는 것도 가능하다. 이런 것은 Activity의 일부분을 어떤 기준에 따라 나누는 것이 가능하게 하여, Activity의 화면 구성을 좀 더 유연하게 만들고 어떤 부분에서 관리도 더 쉽게 만들 수 있다.
Fragment의 특징
1. Fragment는 독립적일 수 없다.
- Fragment는 Activity나 다른 부모 Fragment에 호스팅(종속)되어야 한다. 즉, Fragment는 독자적인 layout.xml을 가질 수 있지만, Fragment 객체가 생성되고 화면에 그려지는 것은 호스트 Activity나 호스트 Fragment 내에서 이루어진다. 이때 Fragment는 호스트 Activity나 Fragment의 뷰 계층에 속하게 되며, 호스트 Activity나 Fragment에 의해 호스팅되여야 하며, 그 역할을 수행한다.
- 위 그림에서 Level은 Activity가 Fragment를 포함하고 있고, Fragment가 또 다른 하위 Fragment를 갖고 있을 때 나타난다. 항상 호스트하고 있는 족은 FragmentManager라는 클래스의 객체를 통해 하위 Fragment들을 관리할 수 있다.
2. Fragment는 자체적인 생명주기를 가진다.
- 이에 관련된 이야기는 밑 "Fragment 생명주기"에서 설명한다. Activity나 Fragment가 시작됨 상태 이상일 때만 Fragment를 추가, 교체, 삭제할 수 있고, 이 과정에서 자체적인 생명주기를 갖는다.
3. Fragment는 재사용이 가능하다.
- 어떤 Fragment의 객체는 Activity나 다른 Fragment에 의해 호스팅 되지만, 이 말이 하나의 Activity나 Fragment에서만 생성할 수 있다는 것은 아니다. 한 Fragment의 객체는 다수의 Activity에서 생성할 수 있다.
4. Activity 내에서 실행 중 추가, 삭제, 교체 등이 가능하다.
Fragment 생성하기
< 나중에 추가할 예정이다.>
Fragment Lifecycle (생명주기)
2020.03.18. 업데이트 이전 Fragment 생명주기
Fragment의 생명주기를 관리하는 것은 Activity의 생명주기를 관리하는 것과 매우 비슷하다. Fragment는 항상 Activity 내에서 구축되어야 하며, 해당 Fragment의 생명주기는 호스트 Activity의 생명주기에 직접적으로 영향을 받는다. 예를 들어, Activity가 일시 정지되는 경우, 그 안의 모든 Fragment도 일시 정지 되며 Activity가 소멸되면 모든 Fragment도 마찬가지로 소멸된다.
Activity와 Fragment의 생명주기에서 가장 큰 차이점은 해당되는 백 스택에 저장되는 방법에 있다.
- Activity는 정지되면 시스템에서 관리하는 Activity의 백 스택에 들어간다.
- Fragment는 이를 제거하는 트랜잭션에서 addToBackStack()을 호출하여 인스턴스를 저장하라고 명시적으로 요청할 경우에만 호스트 Activity에서 관리하는 백 스택으로 들어간다.
위에서 설명햇듯이, Fragment가 있는 Activity의 생명주기는 해당 Fragment의 생명주기의 직접적인 영향을 미친다. 따라서 Activity에 대한 생명주기 콜백이 Fragment에 대한 비슷한 콜백을 발생시킨다. 예를 들어, Activity가 onPause()를 받으면 해당 Activity 내의 각 Fragment가 onPause()를 받는다.하지만 프래그먼트에는 프래그먼트의 UI를 빌드하고 소멸시키는 등의 같은 작업을 수행하기 위해 액티비티와의 고유한 상호작용을 처리하는 몇 가지 수명 주기 콜백이 더 있다.여기서 추가 콜백 메소드는 밑 설명에서 (☑️) 표시를 해두도록 하겠다.
1. onAttach() ☑️
onAttach() 메소드는 Fragment가 Activity에 연결될 때 호출되는 메소드이다. 이때까지는 Fragment가 완전히 생성된 것이 아니다. 호출되면서 인자로 context가 주어진다.
2. onCreate()
onCreate() 메소드는 Activity가 Fragment를 생성할 때 호출되는 메소드이다. Activity의 onCreate() 메소드와 마찬가지로 초기화 작업을 이 메소드에서 실행한다. 하지만, Activity와 다르게 Fragment의 onCreate()에서는 View와 관련된 UI 작업을 할 수 없다. Fragment 생성 시 넘겨받는 값이 있다면 이 메소드에서 변수에 넣어주면 된다.
3. onCreateView() ☑️
Fragment와 연결된 View 계층을 생성하기 위해 호출된다. onCreateView() 메소드는 Fragment가 UI를 그릴 때 호출되는 메소드이다. 레이아웃을 inflate해서 반환해주고, View 객체를 얻을 수 있으므로 UI와 관련된 Binding 작업을 실행하면 된다. Fragment의 레이아웃 루트이기 때문에, UI를 제공하지 않을 경우에는 null을 반환하면 된다. onCreateView()의 매개변수로 전달되는 container가 Activity의 ViewGroup이며, 여기에 Fragment가 위치하게 된다. 또 다른 매개변수인 savedInstanceState는 Bundle 객체로 Fragment가 재개되는 경우 이전 상태에 대한 데이터를 제공한다.
4. onStart()
onStart() 메소드는 Activity가 시작됨 상태에 들어가면 호출되는 메소드이다. Activity와 마찬가지로 Fragment가 화면에 보이기 바로 직전에 호출한다. 사용자에게 Fragment가 보이게 되고, 이 메소드에서 UI를 관리하는 코드를 초기화한다. 이 메소드는 매우 빠르게 완료되고, 완료되면 재개됨(Resumed) 상태로 들어가 onResume() 메소드를 호출한다. 여기서 Activity는 시작됨 상태이다.
5. onResuem()
onResume() 메소드가 호출되면 화면에 보여지며 사용자와 상호작용한다. 어떤 이벤트가 발생하여 포커스가 떠날 때까지 이 상태에 머무른다. 프로그램이 일시 정지 되어 onPause()를 호출하고 다시 재개되면 onResume() 메소드를 다시 호출한다. 재개 상태로 전환될 때마다 필요한 초기화 작업들을 수행해야 한다. Activity와 마찬가지로 이벤트가 발생하여 Fragment가 가려지기 전까지 이 상태가 유지된다.
6. onPause()
onPause() 메소드는 사용자가 Fragment를 떠나면 첫 번째로 호출되는 메소드이다. 사용자가 돌아오지 않을 수도 있으므로 여기에 UI 관련 처리를 정지하고, 중요한 데이터를 저장한다. 다른 Fragment가 화면을 가리게 되어 기존 Fragment의 화면이 사라지게 되는 시점에서 호출한다. 기존 레이아웃은 백스택으로 들어간다. 부모 Activity가 아닌 다른 Activity가 위로 올라오거나, 다른 Fragment가 add 되는 경우 일시정지 상태로 들어간다.
7. onStop()
onStop() 메소드는 Fragment가 완전히 가려지는 경우 onPause()에 이어 호출되는 메소드이다. 사용자가 다시 Activity를 호출하면 다시 복원될 수 있는 상태이다. 기능을 완전히 상실함을 의미한다. 시스템에서 onStateInstance()를 호출하여 UI의 상태를 저장하므로 Activity를 다시 띄우면 이전 상태가 그대로 보여진다.
8. onDestoryView() ☑️
Activity의 onCreate() 메소드가 반환할 때 호출된다. onDestoryView() 메소드는 Fragment와 관련된 View가 제거될 때 호출되는 메소드이다. 즉, Fragment의 View 리소스를 해제되는 구간이라는 것이다. Activity에서 Fragment 생성 시 addToBackStack() (백스택)을 요청했을 경우 onDestory()를 호출하지 않고 인스턴스가 저장되어 있다가 Fragment를 다시 부를 때 onCreateView()를 실행하여 다시 화면에 보여지게 된다.
9. onDestroy()
onDestroy() 메소드는 Activity 또는 Fragment가 소멸되기 전에 호출되는 메소드이다. Fragment가 생성될 때, onCreate() → onCreateView()로 초기화할 리소스들과 뷰를 순서대로 호출했는데, Destory할 때는 반대로 onDestoryView() → onDestory() 순으로 뷰를 제거하고 호출한다.
8. onDetach() ☑️
onDetach() 메소드는 Fragment가 완전히 소멸되고, Activity와의 연결도 끊어질 때 호출되는 메소드이다.
2020.03.18. 업데이트 이후 Fragment 생명주기
200318 업데이트 이후 Fragment의 onActivityCreated()가 지원 중단(deprecated)되었다고 한다!!!!!!
기존의 onActivityCreated가 사라지고 새로운 메소드들이 생겼다.
기존 생명주기와 달라진 점은 무엇이고 onActivityCreated의 빈자리는 어떻게 채워졌을까?
왼쪽 (기존 생명 주기) / 오른쪽 (새로운 생명 주기)
기존 생명주기와 달라진 점
onActivityCreated가 지원 중단되고, 3가지 새로운 메소드 (onViewCreate(), onViewStateRestored(), onSaveInstanceState())가 추가되었다.
onViewCreate()
Fragment의 레이아웃(View, 뷰)이 생성된 후의 호출된다(onCreateView()가 반환된 이후 바로 실행). onCreateView()를 통해 반환된 View 객체는 onViewCreate()의 파라미터로 전달되는데, 이 시점부터 Fragment View의 생명 주기는 초기화 상태로 업데이트 됐기 때문에 이곳에서 View의 초기값을 설정하는 로직이 들어간다. 예를 들어, Live Data observing 코드, RecyclerView 혹은 ViewPager2에 사용할 Adapter 초기화 등이다.
onViewStateRestored()
Fragment에 속하는 View들의 상태 값들을 모두 읽어왔을 때 호출된다. 저장해둔 모든 상태 값(State)이 Fragment의 View 계층 구조에 복원 됐을 때 호출된다. 예를 들어, checkBox 위젯에 체크가 되어있는지 등 각 View 의 상태값들을 체크할 수 있다.
onSaveInstanceState()
Fragment의 상태 값(State)이 저장된 이후에 호출되는 메소드이다. API 28 버전 이전에는 이 메소다 호출되고 onStop()이 호출되었다. 따라서 onStop()에서도 이미 Fragment의 상태 값이 저장된 후였다. 하지만 API 28 버전 이후에는 onStop()이 먼저 호출되고 이 메소드가 호출된다. 이때에는 onStop()에서 아직 상태가 저장되지 않은 상황이다. 이 메소드가 호출되는 시점에서야 상태가 저장되게 된다.
업데이트 함으로써 Activity 생명주기와 Fragment 생명주기가 서로 영향을 덜 주고 깔끔하게 실행된다. 또, 기존에는 onActivityCreated()에서 Fragment 관련 초기화 루틴을 거의 실행했었는데 이를 어느정도 분리한 것으로 보인다.
onActivityCreate()의 지원 중단
2020년 3월 18일 Android 업데이트 이후 Fragment의 onActivityCreated()가 deprecated 됐다. 그로인해 Activity의 생명주기와 Fragment의 생명주기가 서로 영향을 덜 주는 방향으로 개편되었다. 원래 onActivityCreated()의 존재 이유 자체가 Activity의 생성이 끝나야만 처리되어야 하는 로직이 Fragment에 있을 때 위한 것이다. 기존에는 onActivityCreated()에서 Fragment 관련 초기화 루틴을 거의 수행했었는데 onActivityCreated()가 사라지면서 공식문서에서는 View 관련 초기화는 onViewCreated(), 나머지 초기화 후은 onCreate()에서 수행하라고 나와있다.
생명주기 메소드 호출 순서 (업데이트 이후 기준)
1. Fragment 최초 실행
onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()
- 최초의 Fragment가 호스트 Activity에 부착되고, FragmentManager에 추가되면서 onAttach()가 호출된다.
- 초기화가 이루어지는 onCreate()가 호출된다.
- 뷰를 inflate하는 onCreateView()가 호출된다.
- 뷰가 생성된 시점에 onViewCreated()가 호출된다.
- Fragment가 사용자에게 보이는 시점에 onStart()가 호출된다.
- 사용자와 상호작용 가능한 시점에 onResume()가 호출된다.
2. 다른 Fragment로 이동 (Fragment A → Fragment B)
onPause() → onStop() → onDestroyView()
⇒ 이때 위 과정은 Fragment A의 과정이며 Fragment B의 과정은 “1. Fragment 최초 실행” 부분이다.
- Fragment A : 사용자와 상호 작용이 불가해지며 onPause()가 호출된다.
- Fragment A : 화면에서 보이지 않게 되며 onStop()가 호출된다.
- Fragment B : 일반적인 Fragment 생성 순서대로 생명주기 함수를 호출한다. (1. Fragment 최초 실행)
- Fragment A : Fragment B가 사용자와 상호작용 준비를 마치면(즉, onResume() 함수 호출되면), 분리된 뷰가 제거되면서 Fragment A의 onDestroyView()가 호출된다.
3. Fragment B에서 뒤로가기 Button을 눌러 다시 Fragment A로 이동
Fragment A 과정 : onCreateView() → onViewCreated() → onStart() → onResume()
Fragment B 과정 : onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()
- Fragment B : onPause(), onStop()을 차례로 호출한다.
- Fragment A : Fragment가 제거된 것이 아니라 백스택에 유지되어 있기 떄문에 onAttach(), onCreate()가 실행되지 않고, 바로 onCreateView()가 실행되어 뷰를 다시 연결한다.
- Fragment A : 화면에 표시되는 시점에 onStart()가 호출된다.
- Fragment A : 사용자와 상호 작용 가능한 시점에 onResume()가 호출된다.
- Fragment B : onDestroyView(), onDestroy(), onDetach()를 차례로 호출하며 백스택에서 자신을 제거한다.
4. 화면을 닫은 경우 (앱 종료 등)
onPause() → onStop() → onDestroyView() → onDetach()
- 사용자와 상호 작용 할 수 없는 시점에 onPause()가 호출된다.
- 화면에 더 이상 보이지 않게 되는 시점에 onStop()가 호출된다.
- 뷰가 제거되는 시점에 onDestroyView()가 호출된다.
- Fragment가 FragmentManager에서 제거되고 메모리에서 해제되는 시점에 onDetach()가 호출된다.
onCreateView 와 onViewCreated
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search_theme, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
onViewCreated()는 onCreateView()에서 return 해준 view를 가지고 있다. 즉, View가 완전히 생성된 경우이다.
둘의 차이점은?
- onCreateView()는 뷰를 만들 때 실행할 내용이 들어간다.
- onViewCreated()는 뷰가 만들어지면 실행할 내용이 들어간다.
'Android' 카테고리의 다른 글
[Android] ViewBinding, DataBinding에 대해 알아보자! (0) | 2023.07.27 |
---|---|
[Android] Activity, Activity Lifecycle에 대해 알아보자! (0) | 2023.07.04 |