Navigation을 활용하면 각각의 multiple backstack을 활용하고자 할때 각각의 백스택을 만들어 주곤 하는데, 내부적으로 어떻게 동작하는지 궁금하여 공부하게 되었다. 

보통 이렇게 아래에 Bottom Navigation을 만든다.

 

궁금증?

  • 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

// 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

 

728x90
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기