[Android] Jetpack Navigation 백스택관리

Janzizu·2023년 4월 24일
0
post-thumbnail

오늘은 Jetpack Navigation을 쓰면서 느낀 점에 대해 글을 쓰려고 한다.

우선, jetpack의 네비게이션을 쓰기 전에 많은 사람들이 아래와 같은 방식으로 프래그먼트 전환을 시켰을 것이라 생각한다.

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

Fragment aFragment = new Fragment();
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();

나 또한 위와같은 코드를 이용하여 프래그먼트 전환을 시켰다.
jetpack의 navigation이 나오고서는 잘 쓰지 않았는데, 더 간결한 코드로 짤 수 있게 해줬기 때문인 것 같다.

Jetpack Navigation을 쓰면서 항상 들었던 혹은 보았던 말이,

컨트롤러가 알아서 다 관리해준다!

였다.
나는 바보같이 이 말만 듣고서는 백스택 관리를 전혀 해주지 않았다.

이게 무슨말이냐 하면,

jetpack navigation을 구현할 때

graph xml을 작성하여 프래그먼트끼리 연결하여 액션을 만들어주고,

코드상에서 ( a -> b)

findNavController().navigation(R.id.action_aFragment_to_bFragment)

와 같이 프래그먼트 전환을 시켜주었다.

과연 저런식으로만 끝내도 되는 걸까?

아니다!
예를들어 생각해보면 총 4개의 A,B,C,D 프래그먼트가 있고
A -> B -> C -> D -> C -> B -> A 와같이 프래그먼트 전환이 일어날 때
'그저' 위의 코드와 같이 작성해주면 프래그먼트가 계속하여 백스택에 쌓이게 된다.
결국 이것은 메모리 누수를 초래할 수 있다.

그럼 어떻게 해결할 수 있을까?

나의 경우엔 popUpTopopUpToInclusive를 이용하여 해결하였다.

popUpTo는 쌓인 모든 프래그먼트를 제거하고 지정된 프래그먼트까지 이동하는데에 사용한다.

popUpToInclusive는 popUpTo와 비슷하지만, popUpTo를 포함한 프래그먼트를 스택에서 제거하냐 안하냐를 정한다.

더 자세히 예를들어보자,

splashFragment -> loginFragment -> mainFragment 의 흐름을 가진 앱이 있다고 가정해보면,

splanFragment 에서 LoginFragment까지 이동하는 액션이 아래와 같다.

   <fragment
        android:id="@+id/splashFragment"
        android:name="com.android.splashFragment"
        android:label="splashFragment"
        tools:layout="@layout/fragment_splash">
        <action
            android:id="@+id/action_splashFragment_to_LoginFragment"
            app:destination="@id/loginFragment"
            app:popUpTo="@id/splashFragment"
            app:popUpToInclusive="true"/>
    </fragment>

예를들어 소스가 위와같이 구성되어있다면,
사용자가 로그인 화면으로 이동하면, 이전에 있던 스플래시 화면이 스택에서 제거(pop)되도록 지정하는 것이다. 즉, 위의소스만 토대로 말하자면 로그인화면에서 popToBackStack()을 하면 이미 스플래스 화면은 백스택에서 제거됐기 때문에 돌아갈 수 없는 것이다.

프래그먼트 전환이 되지 않았던 경험

navigation을 이용하여 프래그먼트 전환을 하고 있는데, 아래와 같은 메시지가 뜨면서 전환이 제대로 이루어 지지 않았다.

fragmentmanager has already saved its state
(FragmentManager이 이미 상태 저장을 수행한 후에 프래그먼트 전환이 발생했다는 것 같다)

환경은 이랬다.
A Fragment -> B Fragment -> 외부앱 호출 -> 외부앱으로부터 상태값을 받고 -> A Fragment로 이동

아무리 찾아도 해결책을 찾지 못 했을 무렵 왜 안될까? 생각을 해보니
내 앱은 지금 포그라운드 상태가 아니며 액티비티 자체가 onStop상태였다.

아~ 그럼 onStop상태에서는 프래그먼트 전환이 이루어 지지 않는건가.....?
그게 호출되면서 프래그먼트매니저에 의해 상태가 저장되는건가?
찾아보니 onStop이라고 프래그먼트 전환이 이루어지지 않는건 아니라고 하는데 이 부분은 조금 더 찾아보아야 할 것 같다.

이 부분에 있어서는 조금 우회적인 방법으로 해결을 하였는데,
액티비티를 singleTask로 하나의 인스턴스만 존재하게끔 처리하여
외부 앱으로부터 값을 받으면 액티비티를 재실행시켜 onNewIntent()를 호출하였고,

다행히도 onNewIntent() 메소드 안에서는 프래그먼트 전환이 문제없이 이루어졌다.

0개의 댓글