안드로이드 addToBackStack

안드로이드 addToBackStack

2022, Feb 26    

[Android] FM addToBackStack에 관한 고찰

addToBackStack

궁금해서 로그를 찍어보았던 addToBackStack을 정리해보려고 한다.

  supportFragmentManager
    .beginTransaction()
    .replace(R.id.fcv_container, fragment)
    .addToBackStack(name = null)
    .commit()    

이렇게 replace로 fragment를 추가한뒤에, remove(fragment)를 실행했을때의 결과가 내가 생각하던 결과와 달라서 포스팅을 시작하게 되었다.

나는 당연히 remove가 실행되면서 fragment가 완전히 destroy 될줄 알았지만, 결과는 fragment는 destroyView까지만 실행이 됐다.
그래서 뷰모델이 살아있거나 라이프사이클을 제대로 관리하지 못했다.
이때 addToBackStack을 제거하면, 내가 생각했던것 처럼 remove시 fragment가 destoy된다.

fragmentManager

안드로이드에서 fragment를fragmentManager를 사용하여 조작한다. 그리고 beginTransaction으로 add,replace, remove 등을 transaction단위로 commit을 적용을 시킨다. transaction이라는 단위를 기억해두자.

add와 replace 차이

  • add(A): add Fragment A
  • replace(A): remove(기존 프레그먼트 컨테이너의 존재하는 fragment들) -> add Fragment A

다시 본론으로 돌아와서 addToBackStack은 언제 쓰이는지를 먼저 보면
말그대로 FragmentManger의 백스택에 add 할때 사용한다.
그래서 뒤로가기키를 누르면 onbackpressed가 실행되면서 백스택에 있는 add된 무언가를 가져온다는 것이다.
(뒤로가기 키를 누르면 내부적으로 백스택에 무언가 존재하면 이를 pop해주는 popBackStack를 실행시키기 때문)

여기서 난 궁금증이 들었다. 무엇을 add할까? 당연히 fragment를 저장한다고 생각했다.
하지만 transaction을 저장한다고 메소드 설명에 나와있다.

addToBackStack

Add this transaction to the back stack.
This means that the transaction will be remembered after it is committed,
and will reverse its operation when later popped off the stack.

마지막 문장에 주목해 보자면, 스택에서 pop될때 transaction의 동작을 reverse 한다고 적혀있다.
즉 transaction을 반대로 수행한다는 것이다.

이를 테스트 해보려고 fragment를 2개를 add한후(A, B), replace.addToBackStack(C)을 한뒤 뒤로가기 키를 눌러보았다.

로그 결과

replace시에 B,A가 차례로 DestroyView 된다. (add와 replace의 차이점에서 설명한 replace의 remove 부분)
그 다음 C가 onResume되어 보여지고. (replace의 add부분)

뒤로가기를 누르면

C가 Destory되고, (replace의 add부분의 reverse인 remove가 실행)
A,B가 차례로 onResume된다. (replace의 remove부분의 reverse인 add가 실행)

결과를 보면 뒤로가기 시에 정확히 addToBackStack으로 넣었던 transaction(replace)의 반대되는 동작을 하게된다.

두번째로 내가 궁금했던 경우도 해보았다. fragment를 replace.addToBackStack 후 remove를 해봤다.
이때 remove를 하면 destroyView까지만 호출이 된것을 확인했다.
그리고 뒤로가기 키를 누르자 destroy가 호출되는것을 확인했다.
이는 백스택에 있던 replace(remove후 add)의 역순으로 remove fragment가 실행되면서 destroy가 호출된거고, replace의 역순 remove다음인 add는 아무것도 없는 상태였기 때문에 일어나지 않는다.

마지막으로 add없이 remove.addToBackStack 실행후 뒤로가기 키를 누르는 경우도 해봤다.
transaction을 역으로 실행한다면 뒤로가기 키를 누르면 add하지 않았던 fragment가 remove의 역인 add가 되지 않을까? 라는 궁금증이 생겨서다.
remove를 해도 fragment가 기존에 있지 않았기 때문에 로그가 찍히지 않았다.
여기서 뒤로가기 키를 누르면 내 예상대로, fragment가 onResume이 호출되는것을 확인했다😀
하지만 프레그먼트가 보이진 않는데, 어떤 container에 add할지 몰라서 그런것 같다.
(remove시에는 container를 매개변수로 받지않는다. 동일한 인스턴스의 fragment는 2개 이상의 container에 존재 할 수 없어서 remove시 알아서 제거)
그리고 다시한번 뒤로가기를 누르면 액티비티가 닫히면서 destroy가 호출되었다.

정리

  • addToBackStack은 transaction 단위를 저장한다.
  • 그리고 뒤로가기 키를 누르거나 popBackStack 실행시 백스택에 저장된 transaction을 pop해서 역순으로 동작시킨다. (add였으면 remove, replace였으면 remove후 add)
  • 백스택에 addToBackStack을 통해 transaction단위 안에 fragment가 남아있으면 프론트쪽에서 remove를 해도 그 fragment는 destroyView까지만 실행이된다고 파악됐다.(이 부분은 로그상 그렇게 보이기에)

추가로 addToBackStack(name)에서 name이 transaction의 name인데, 이를 통해 해당 transaction에 접근할 수 있다.