본문 바로가기
Android/Android 기초

[Android] Fragment Manager

by 태크민 2024. 12. 16.

Fragment 이동 처리 관련해서는 Jetpack의 Navigation Component를 쓰고 있지만, FragmentMananger의 동작 과정에 대해서 잘 이해하고 있지 못한 것 같아 포스팅을 해본다.

 

FragmentManager

  • FragmentManager는 앱 프래그먼트에서 프래그먼트를 추가, 삭제, 교체하고 백스택에 추가하는 작업을 실행하는 클래스이다.
  • Activity 또는 Fragment에서 엑세스 할 수 있으며, FragmentActivity 및 서브 클래스에서 접근이 가능하다.
    • getSupportFragmentManager(), childFragmentManager(), parentFragmentManager()

 

FragmentManager 접근

  • Activity에서 접근 방법
    • FragmentaActivity 및 이를 상속한 AppcompatActivity에서 supportFragmentManager() 호출
  • Fragment에서 접근
    • 어떤 프래그먼트가 하위 프래그먼트를 호스팅하는 경우 호스트 프래그먼트 내에서 getChildFragmentManager() 호출
    • 하위 프래그먼트에서 호스트 Fragment를 관리하는 FragmentManager를 호출할때 getParentFragmentManager() 호출

 

주요 역할

  • FragmentTransaction
  • Fragment 추가, 제거, 교체
  • BackStack 관리

 

FragmentTransaction

  • FragmentManager를 사용하여 Fragment를 추가, 제거, 교체 등의 작업을 수행하는 일련의 과정을 의미한다.
    • 데이터베이스에서와 같이 일련의 작업들을 묶어 처리하는 개념으로 Fragment 관련 작업을 한 번에 처리하고 성공적으로 완료되면 commit할 수 있다.
  • FragmentTransaction 객체를 사용해서 Fragment Transaction Method 작업을 진행할 수 있으며, 여러 작업을 한 번에 묶어 트랜잭션 단위로 처리할 수 있다.
  • 또한 FragmentTransaction은 BackStack에 추가할 수 있어, 사용자가 뒤로가기 버튼을 눌렀을 때 이전 상태로 돌아갈 수있다.

Fragment Trasaction Method

  • add() : 새로운 Fragment 추가
  • replace() : 기존의 Fragment 교체
  • remove(): Fragmnet 제거
  • hide() : Fragmnet를 화면에서 숨김
  • show() : 숨겨진 Fragment 표시
  • attach() : Fragment를 다시 화면에 표시
  • detach() : Fragment를 화면에서 분리

 

add vs replace

1. add

<1. 프래그먼트 A add()>
Fragment A: onAttach() ▶️ onCreate() ▶️ onCreateView() ▶️ onStart() ▶️ onResume()
<2. 프래그먼트 B add()>
Fragment A: (유지)
Fragment B: onAttach() ▶️ onCreate() ▶️ onCreateView() ▶️ onStart() ▶️ onResume()

2. replace

<1. 프래그먼트 A add()>
Fragment A: onAttach() ▶️ onCreate() ▶️ onCreateView() ▶️ onStart() ▶️ onResume()
<2. 프래그먼트 B replace()>
Fragment A: onPause() ▶️ onStop() ▶️ onDestroyView() ▶️ onDestroy()  ▶️ onDetach()
Fragment B: onAttach() ▶️ onCreate() ▶️ onCreateView() ▶️ onStart() ▶️ onResume()

참고로, addToBackStack()을 사용했을 경우 replace()를 호출하여 기존 fragment에 대한 remove()가 호출되어도 onDetach()까지 호출되지 않고 onDestroyView()까지 호출된다. 

간단하게 이야기 하면 addToBackStack()으로 인해 Fragment가 중단되느냐 아니면 소멸되느냐의 차이라고 한다.

addToBackStack()을 호출하여 백 스택에 fragment를 추가하였기 때문에 기존 fragment는 remove()가 호출되었지만 소멸되지 않도 중단된 상태에 머무르는데, 사용자가 백 버튼을 클릭했을 때 onCreateView()가 호출되면서 상호작용 할 수있는 상태가 된다.

 

commit

  • 각각의 FragmentTransaction은 마지막에 commit을 호출해야합니다. commit() 메서드는 FragmentManager에게 트랜잭션에 모든 명령이 추가되었다고 알린다.
  • 기본적으로 commit()메서드는 비동기적이고, 트랜잭션을 즉시 수행하지 않는다.
    • commit() 호출 시점에 트랜잭션은 메인스레드에 예약된다. 그리고 메인 스레드에서 예약된 트랜잭션의 수행이 가능할 때 트랜잭션이 수행된다.
    • 만약 즉시 트랜잭션이 수행되어야 한다면 commitNow()를 사용하면 된다. 다만 commitNow는 addToBackStack과 호환되지 않는다.
    • commit에는 다양한 메소드를 제공하고 있다. commit / commitNow() / commitAllowingStateLoss()

 

commit vs commitNow

commit : Async - 비동기적으로 트랜잭션을 수행

commitNow: Sync - 동기적으로 트랜잭션을 수행

 

commit()과 혼재해서 사용할 경우 문제가 된다.

왜냐면, commit은 작업을 비동기로 처리하고, commitNow는 작업을 동기적으로 처리하기 때문.

따라서, 순서보장이 안된다.

 

코드를 예를들면,

commitNow()

// Activity.kt
log("Transaction: begin")
supportFragmentManager
        .beginTransaction()
        .replace(R.id.layout, Fragment2())
        .commitNow()
log("Transaction: end")

// Logcat
Transaction: begin
Fragment2: onAttach
Fragment2: onCreate
Fragment1: onPause
Fragment1: onStop
Fragment1: onDestroyView
Fragment2: onCreateView
Fragment2: onViewCreated
Fragment2: onActivityCreated
Fragment2: onStart
Fragment2: onResume
Transaction: end
Fragment1: onDestroy
Fragment1: onDetach

 

commit

// Logcat
Transaction: begin
Transaction: end
Fragment2: onAttach
Fragment2: onCreate
Fragment1: onPause
Fragment1: onStop
Fragment1: onDestroyView
Fragment2: onCreateView
Fragment2: onViewCreated
Fragment2: onActivityCreated
Fragment2: onStart
Fragment2: onResume
Fragment1: onDestroy
Fragment1: onDetach

 

commitAllowingStateLoss

Fragment를 사용하는 안드로이드 개발자들은 onSaveInstanceState() 이후 커밋을 수행할 수 없다는 IllegalStateException 을 마주한 경험이 있을 것이다.

문제의 원인은 Activity의 onSaveInstanceState()가 호출된 후에 FragmentTransaction의 commit()을 동작하는 데에 있다.

 

onSaveInstaceState() 호출로 상태를 save (= 상태A) 했는데 이를 restore할 새도 없이 여기다가 다른 트랜잭션을 commit(= 상태A + 다른 트랜잭션 = 상태B) 때려버린다?

이러고 다시 포그라운드로 왔을 때를 상상해보자.
상태를 restore하려고 하는데, 내가 예상한 상태는 상태B였는데, 상태A가 restore 될 것이다.

이런 상태 손실을 막기 위해, 안드로이드는 이미 저장된 상태가 있을 때 commit을 하게 되면 IllegalStateException를 뱉는 것이다.

 

그럼 어떻게 해야 해요?

1. 상태 손실이 괜찮다? = commitAllowingStateLoss를 호출하면 된다.

supportFragmentManager.commit(allowingStateLoss = true){
    //수행할 트랜잭션
}

 

2. 이렇게 하자

onStart 이후로 뷰가 완전히 restore되었을 때, 또 onSaveStateInstance 호출되기 전에 그 구간에만 트랜잭션 commit을 호출하면 된다.

즉, onStart - onPause 사이에만 호출되도록!

 

BackStack 관리

  • BackStack은 Fragment 트랜잭션을 기록하여, 사용자가 뒤로 가기 버튼을 눌럿을 때 이전 상태로 돌아갈 수 있게 하는 기능이다.
  • addToBackStack() 메서드를 호출하여 트랜잭션을 BackStack에 추가할 수 있다.
fragmentTransaction.replace(R.id.fragment_container, newFragment)
fragmentTransaction.addToBackStack(null)  // 트랜잭션을 BackStack에 추가
fragmentTransaction.commit()

 

 

정리

  • FragmentManager는 Android에서 Fragment의 라이프사이클을 관리하고 트랜잭션을 처리하는 도구이다.
  • Fragment Tracnsaction은 FragmentManager를 사용하여 Fragment를 동적으로 추가, 제거, 교체하는 작업을 트랜잭션 단위로 처리하는 방식이다.
  • 이들을 통해 UI 전환을 더 유연하게 관리할 수 있으며, BackStack을 사용하면 이전 상태로 돌아가는 동작을 지원할 수 있다.

참고자료

https://kumgo1d.tistory.com/entry/Android-Fragment-Manager%EC%97%90%EC%84%9C-replace%EC%99%80-add%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90

 

[Android] Fragment Manager에서 replace()와 add()의 차이점

안녕하세요 골드입니다. 오늘은 Fragment에서 사용하는 함수 add()와 replace()에 대해 글을 작성하겠습니다. 면접에서 이 부분을 애매하게 알면서 대답을 못했습니다. 간단하게 알고 있었던 부분은 ad

kumgo1d.tistory.com

https://zzandoli.tistory.com/55

 

[안드로이드 Q&A] 프래그먼트에서 add()와 replace()의 차이점이 무엇일까?

프래그먼트를 주로 사용하면서 add()와 replace()가 과연 어떤 점이 차이점이 있는지 항상 궁금했다. 그래서 각 역할에 대해서 알아보고, 차이점을 정리해보기로 했다. replace()는 아래와 같이 사용된

zzandoli.tistory.com

https://velog.io/@suev72/%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8-2-commitcommitNowallowingStateLoss

 

프래그먼트 - 2 commit/commitNow/allowingStateLoss

java.lang.IllegalStateExeption: Can not perform this action after onSaveInstatnceState문제의 원인은 Activity의 onSaveInstanceState()가 호출된 후에 FragmentTransact

velog.io

https://hooun.tistory.com/277

 

[Android] Fragment commit commitNow 차이

Fragment commit vs commitNow commitNow() 동기적으로 트랜잭션 처리. // Activity.kt log("Transaction: begin") supportFragmentManager .beginTransaction() .replace(R.id.layout, Fragment2()) .commitNow() log("Transaction: end") // Logcat Transaction:

hooun.tistory.com

https://jinudmjournal.tistory.com/237

 

[Android] FragmentManager & FragmentTransaction

💡FragmentManager & FragmentTransaction의 역할을 이해하고 활용하는 방법에 대하여 기록하였습니다. FragmentManager프래그먼트 관리자  |  Android Developers 프래그먼트 관리자  |  Android Developers이 페이지

jinudmjournal.tistory.com