Navigation을 활용하면 각각의 multiple backstack을 활용하고자 할때 각각의 백스택을 만들어 주곤 하는데, 내부적으로 어떻게 동작하는지 궁금하여 공부하게 되었다.
궁금증?
- fragment 를 전환시키는 과정을 해보다 보면 fragment를 전환할 시에 전환되는 화면은 메모리상에서 내려가게 되는데 navigation을 통한 fragment backstack은 어떻게 이전 정보들을 그대로 가지고 있는걸까?
- fragment backstack이 어떻게 각각의 navigation bar마다 생기는걸까?
시작하기 앞서
시작하기에 앞서 기본적으로 fragment를 전환시키는 방법을 알아보자.
// This is the initial fragment the user sees
fragmentManager.commit {
setReorderingAllowed(true)
replace<HomeFragment>(R.id.fragment_container)
}
// Later, in response to user actions, we’ve added two more
// transactions to the back stack
fragmentManager.commit {
setReorderingAllowed(true)
replace<ProfileFragment>(R.id.fragment_container)
addToBackStack(“profile”)
}
fragmentManager.commit {
setReorderingAllowed(true)
replace<EditProfileFragment>(R.id.fragment_container)
addToBackStack(“edit_profile”)
}
보통 위처럼 fragment manager 를 통해서 fragment를 전환하고 backstack에 이름을 적어 저장하곤 한다.
Fragment Backstack in Navigation Jetpack
네비게이션 백스택은 새롭게 바뀐 fragment manager기능을 통해 관리되는데 이는 기존의 fragment replace와 같지만 다른 점이 존재한다.
Saved State
Saved State라는 공간을 활용하여 Back Stack의 fragment들의 상태를 저장하는 saveBackStack() 을 navigation jetpack에서 지원하기 때문이다. 이는 아래와 같이 생각할 수 있다.
fragmentManager.saveBackStack("profile")
fragmentManager.commit {
setReorderingAllowed(true)
replace<NotificationsFragment>(R.id.fragment_container)
addToBackStack("notifications")
}
위 코드를 적용하면
위 그림처럼 바뀌게 된다. (이 상태는 fragment 의 직접적인 상태를 저장한것이 아닌 Saved State 를 저장한것이다)
그리고 위 "profile" 을 불러오면
fragmentManager.saveBackStack(“notifications”)
fragmentManager.restoreBackStack(“profile”)
위처럼 "profile 에 존재하던 fragment 들이 다시 backStack으로 올라오게 된다.
즉, 기존의 addToBackStack 과 popUpBackStack 이 한쌍으로 사용되었다면, navigation backstack은 saveToBackStack 과 restoreBackStack 으로 바뀐 셈이다. 그리고 이와 같은 변화는 fragment가 바뀌어도 이전 상태를 저장하는 장점이 생기게 되었다. 물론 위와 같은 작업은 Navigation을 세팅하게되면 default로 적용되어 있기 때문에 코드를 직접 작성하지 않아도 된다.
부록 - SavedState - 저장된 인스턴스 상태
Multiple BackStack In Navigation
- 여러개의 Bottom Navigation 은 각각의 Backstack 을 가질 수 있도록 설정이 되어있다. 이를 관리하는 두가지의 영역이 있는데 NavController 와 Navigator 이다.
- NavBackStackEntry: Representation of an entry on the fragment back stack, as created with [FragmentTransaction.addToBackStack()](<https://developer.android.com/reference/android/app/FragmentTransaction#addToBackStack(java.lang.String)>). Entries can later be retrieved with [FragmentManager.getBackStackEntryAt()](<https://developer.android.com/reference/android/app/FragmentManager#getBackStackEntryAt(int)>)
- NavController는 각각의 NavBackStackEntry 인스턴스를 관리하는 영역을 가지고 있다. (아래 코드 참조)
// navcontroller 클래스 내부 변수
private var navigatorStateToRestore: Bundle? = null
private var backStackToRestore: Array<Parcelable>? = null
private var deepLinkHandled = false
// Navigator 가 원하는 곳의 fragment 를 가져오는 함수
private fun Navigator<out NavDestination>.popBackStackInternal(
popUpTo: NavBackStackEntry,
saveState: Boolean,
handler: (popUpTo: NavBackStackEntry) -> Unit = {}
) {
popFromBackStackHandler = handler
popBackStack(popUpTo, saveState)
popFromBackStackHandler = null
}
Navigator는 BackStackEntry와 관련된 Navigator 의 상태를 저장한다. (Navigator는 어떤 프래그먼트로 가야할지 알고있는 클래스이다.) (Navigators should be able to manage their own back stack when navigating between two destinations that belong to that navigator. The NavController manages a back stack of navigators representing the current navigation stack across all navigators)
//navigator 클래스 내부
//The state of the Navigator is the communication conduit between the Navigator
//and the NavController that has called onAttach.
private var _state: NavigatorState? = null
protected val state: NavigatorState
get() = checkNotNull(_state) {
"You cannot access the Navigator's state until the Navigator is attached"
}
Mutiple BackStack을 사용하게 되면 그대로 상태를 저장할 수 있는 메서드들이 모두 준비되어 있음을 볼 수 있다. 하지만 라이브러리 버전이 아래 버전보다 낮다면 위 기능을 지원하지 않는다.
versions.fragment = "1.4.0-alpha01"
versions.navigation = "2.4.0-alpha01"
Reference
- https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134
- https://developer.android.com/guide/navigation/multi-back-stacks
최근댓글