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을 사용하면 이전 상태로 돌아가는 동작을 지원할 수 있다.
참고자료
[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
프래그먼트 - 2 commit/commitNow/allowingStateLoss
java.lang.IllegalStateExeption: Can not perform this action after onSaveInstatnceState문제의 원인은 Activity의 onSaveInstanceState()가 호출된 후에 FragmentTransact
velog.io
[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
'Android > Android 기초' 카테고리의 다른 글
[Android] 태스크 (Task)와 백 스택(Back Stack) (2) | 2024.12.18 |
---|---|
[Android] 왜 Activity 대신 Fragment를 사용해야할까? (0) | 2024.12.16 |
[Android] Theme vs Style (3) | 2024.10.27 |
[Android] 메모리 누수 & 분석 (0) | 2024.06.03 |
[Android] 빌드 변형 구성 (ProductFlavors) 한 개의 프로젝트로 여러개의 앱 만들기 (0) | 2024.02.18 |