[안드로이드] 『프래그먼트 이해하기』

SHY(code poet)·2024년 3월 28일
0
post-thumbnail

1. 액티비티 VS 프래그먼트

액티비티: 탭 버튼과 같이 앱의 사용자 인터페이스에 전역 요소를 배치하기에 적합
프래그먼트: 단일 화면이나 화면 일부의 UI를 정의하고 관리하는 데 더 적합
(Activities are an ideal place to put global elements around your app's user interface, such as a navigation drawer. Conversely, fragments are better suited to define and manage the UI of a single screen or portion of a screen. // 공식문서)

쉬운설명

Fragment = Activity + View
👉 Fragment는 text view, image view와 같이, 액티비티 화면을 구성하는 'VIew'이지만 'Activity'처럼 동작한다. 즉, 액티비티의에 작성할 수 있는 모든 코드는 프래그먼트에도 사용할 수 있다는 것이다.

그럼 Activity를 쓰지 왜 Fragment를 쓰는가?

  1. Activity는 그 자체로 Fragment에 비해 상당히 무겁고, 리소스를 많이 잡아먹는다. 위 사진에서, 5가지 페이지를 모두 서로 다른 Activity로 만든다면, 비효율적이라는 말이다.

  2. 그래서, 하나의 액티비티에 여러가지 프래그먼트를 사용하여 더 효율적으로 만든다.
    👉 Single Activity - Multi Fragment 구조

  3. 또 다른 이점은, Activity를 분할하여 각 부분들을 정의하기에
    유지보수가 편하고, 재사용이 용이하다.

2. 프래그먼트의 특징

① 호스트 뷰 계층 구조의 일부
프래그먼트는 독립적으로 존재할 수 없고, 반드시 Activity나 다른 프래그먼트에 호스팅 즉, 종속되어야 한다.

  • 해당 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 직접적으로 영향을 받는다.
  • 예를 들어, 액티비티가 일시정지되는 경우, 그 안의 모든 프래그먼트도 일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 마찬가지로 소멸된다. 그러나 액티비티가 실행 중인 동안에는 각 프래그먼트를 추가 또는 제거하는 등 개별적으로 조작할 수 있다.
  • ⚠️주의⚠️
    모듈성, 재사용성을 해치지 않기 위해, 프래그먼트 클래스에는 자체 UI를 관리하는 로직만 구현해야 하고 다른 Activity나 다른 프래그먼트를 직접 조작하는 로직을 포함해서는 안된다. 즉, 프래그먼트가 다른 Activity나 프래그먼트에 의존하면 안된다.
  • TIP. TAG 추가하기

② 재사용성
앞서 언급한 바와 같이, 프래그먼트의 존재 이유는 UI 중에서 재사용 가능한 부분을 재사용하기 위함이다.
프래그먼트는 자체 UI를 개별적인 청크로써 사용할 수 있다. 개별 청크 단위로 다른 곳에서 재사용할 수 있는 것이다.

③ 다른 라이브러리와의 호환
Android Jetpack 라이브러리 중 Navigation, BottomNavigationView, ViewPager2는 프래그먼트와 호환되도록 설계되어 있어서 프래그먼트가 해당 라이브러리와 함께 자주 사용된다.

3. 프래그먼트 생명주기

프래그먼트도 액티비티의 생명주기 콜백 함수인
onCreat(), onStart(), onResume(), onPause(), onStop(), onDestroy()를 그대로 가지고 있으며, 호출되는 시점도 액티비티와 같다. 또한, FragmentManager에 의해 주기가 전환된다.
다만, 액티비티에는 없는 몇 단계의 콜백함수가 더 있다.

1. onAttach()

  • 프래그먼트가 엑티비티에 연결되고, 또한 FragmentManager에 관리되기 시작할 때 호출된다.
  • 아직 프래그먼트가 완벽하게 생성된 상태는 아니며, 인자로 context가 주어진다.

2. onCreate()

  • 본격적으로 프래그먼트가 액티비티에 호출을 받아 생성되는 시점이다.
  • Bundle로 액티비티로부터 데이터가 넘어온다.
    Fragment를 생성하면서 넘겨준 값들이 있다면, 여기서 변수에 넣어주면 된다.
  • ⚠️아직 이 시점에서는 View 객체가 생성되지 않았다. 따라서, (액티비티의 onCreate()에선 view나 ui관련 작업을 할 수 있으나,) 프래그먼트의 onCreate()에서는 할 수 없다. 대신 다음에 호출될 콜백 함수에서 binding 및 ui관련 작업을 할 수 있다.

3. onCreateView()

  • 레이아웃을 인플레이트(inflate)하는 곳이다.
  • fragment에서 UI를 그릴 때 호출되는 콜백이며, UI를 구성하는 View를 반환한다.
  • 프래그먼트에 속한 각종 view나 viewGroup에 대한 ui 바인딩 작업을 할 수 있다.
  • 이 메서드는 프래그먼트의 레이아웃 루트이기 때문에 UI를 제공하지 않는 경우에는 null을 반환하면 된다.
  • onCreateView의 매개변수로 전달되는 container가 Activity의 ViewGroup이며, 여기에 Fragment가 위치하게 된다.
  • 또 다른 매개변수인 savedInstanceState()는 Bundle 객체로 Fragment가 재개되는 경우 이전 상태에 대한 데이터를 제공한다.

4. onViewCreated()

  • onCreagteView()를 통해 반환된 View 객체는 onViewCreated()의 파라미터로 전달 된다.
  • 액티비티와 프래그먼트의 뷰가 모두 생성되고 연결된 상태로, View를 변경하는 작업이 가능한 단계이다.
  • 때문에 View(텍스트뷰, 버튼 등)의 초기값 설정, LiveData 옵저빙, RecyclerView, ViewPager2에 사용될 Adapter 세팅은 이 메소드에서 해주는 것이 적절하다.
  • View lifecycle owner: INITIALIZED

5. onViewStateRestored()

  • 저장해둔 모든 state 값이 Fragment의 View의 계층 구조에 복원되었을 때 호출
  • 활용: 현재 체크박스 위젯이 체크되어있는지 각 뷰의 상태값 체크
  • View lifecycle owner : INITIALIZED → CREATED

5. onStart()

  • 프래그먼트가 사용자에게 보이기 시작하는 단계(Visibility)
  • Activity와 같이 Fragment가 화면에 보여지기 직전 빠르게 실행된다.
  • Fragment의 childFragmentManager을 통해 FragmentTransaction을 안전하게 수행할 수 있음
  • 여기서 Activity는 started 상태이다.
  • View lifecycle owner : CREATED → STARTED

6. onResume()

  • 사용자가포커스를 잡은 상태로, 사용자와 프래그먼트가 상호작용할 수 있는 상태일 때 호출
  • Activity와 마찬가지로 이벤트가 발생하여 Fragment가 가려지기 전까지 이 상태가 유지된다.
  • View lifecycle owner : STARTED → RESUMED

7. onPause()

  • 프래그먼트와 사용자와의 상호작용이 중지되었지만, 여전히 visible 상태일 때 호출된다.
  • 부모 Activity가 아닌 다른 Activity가 위로 올라오거나,
    ✨다른 Fragment가 add되는 경우 일시정지 상태로 들어간다.✨
    (이 부분은 아래에 자세히 설명되어 있다.)
  • UI관련 처리를 정지하고, 중요한 데이터를 저장한다.
  • View lifecycle owner: STARTED
    (PAUSED가 아님에 주의. 콜백함수 이름이 onPause()일 뿐, Lifecycle에는 Pause에 해당하는 상태가 없다.)

8. onStop()

  • 프래그먼트는 더이상 보여지지 않게되며(Invisibility), 프래그먼트 기능이 중지될 때 호출된다.
  • Fragment가 완전히 가려지는 경우, onPause()에 이어 onStop()까지 실행된다.
  • 시스템에서 onStateInstance()를 호출하여 UI의 상태를 저장하므로 Activity를 다시 띄우면 이전 상태가 그대로 보여진다.
  • API 28버전을 기점으로 onSaveInstanceState() 함수와 onStop() 함수 호출 순서가 달라져, 더 먼저 호출된다.
    따라서 onStop()이 FragmentTransaction을 안전하게 수행하는 마지막 지점이 된다.
  • View lifecycle owner: STARTED -> CREATED

9. onDestroyView()

  • Fragment와 관련된 view가 제거될 때 실행된다.
  • Activity에서 Fragment 생성 시 addToBackStack()를 요청했을 경우 onDestroy()를 호출하지 않고 인스턴스가 저장되어 있다가 Fragment를 다시 부를 때 onCreateView()를 실행하여 다시 화면에 보여지게 한다.
  • getViewLifecycleOwnerLiveData() 의 리턴값으로 null 이 반환된다.
  • 가비지 컬렉터에 의해 수거될 수 있도록 Fragment View에 대한 모든 참조가 제거되어야 한다.
  • View lifecycle owner: CREATED

10. onDestroy()

  • view가 제거된 후 Fragment가 완전히 소멸되거나, FragmentManager가 destroy됐을 경우 호출된다.
  • Fragment Lifecycle의 끝을 알린다.
  • View lifecycle owner: CREATED -> DESTROYED

11. onDetach()
Fragment가 완전히 소멸되고, Activity와의 연결도 끊어질 때 실행된다.

✨중요✨

Activity Lifecycle의 onPause()가 다른 액티비티에 포커스될 때 발생하는 것이라면,
Fragment Lifecycle의 경우, 이뿐만 아니라, '백 스택'back stack을 사용하는지 여부에 따라 onPause()부분에서 흐름이 다르게 바뀐다.
※백스택: 프래그먼트가 화면에 보이지 않는 순간 제거하지 않고 저장했다가 다시 이용할 수 있는 기능

👉User navigates backward or fragment is removed/replaced
만약 백스택을 사용하지 않으면 프래그먼트가 교체될 때 기존의 프래그먼트는 자동으로 onDestroy까지 호출되어 제거된다.

👉Fragmaent is added to the back stack, them removes/replaced
백스택을 이용하면 프래그먼트가 제거되지 않고 onDestroyView()함수까지만 호출된다.
그리하여 사용자가 기기의 뒤로가기 버튼을 누를 때 이전 프래그먼트 즉, 백스택 화면으로 전환할 수 있다.

💾백스택을 사용하려면 FragmentTransaction의 addToBackStack()함수를 이용해야 한다.

transaction.addToBackStack(null)

📜참고자료
fragment와 Fragment의 생명주기
안드로이드 공식문서 파헤치기1
안드로이드 프래그먼트 생명주기
액티비티와 프래그먼트의 생명주기
액티비티 생명주기와의 관계
안드로이드 공식문서 파헤치기2

profile
그것을 이해하고자 하기 때문에 결국은 그것을 견디어내게 된다.

0개의 댓글