Android Fragment 알아보자 2

bang·2022년 3월 2일
0

Fragment

목록 보기
2/2
post-thumbnail

FragmentManager?

Fragment를 추가, 삭제, 교체, 백스택에 추가 등 트랜잭션을 실행하는 프래그먼트를 관리하는 역할을 합니다. 그 외에 Fragment를 찾을수 도 있고, Activity 또는 Fragment와 값을 주고 받을 수 있는 result listener 기능도 있습니다.

프래그먼트의 트랜잭션 관리를 쉽고 편하게 도와주는 Android Jetpack라이브러리의 Navigation 사용이 권장된다고 합니다.


FragmentManager 접근

  • Acrivity에서 접근방법

    • FragmentActivity 및 이를 상속한 AppcompatActivity에서
      supportFragmentManager
  • Fragment에서 접근

    • Fragment는 생성될때 또 다른 개인적인 FragmentManager를 갖게 됩니다. 이를 childFragmentManager

    • Activity에 있는 FragmentManager를 접근하려면 parentFragmentManager

  • 위 그림을 보면 FragmentManager관계를 잘 나타내고 있다. Fragment의 자식 Fragment의 parent는 Activity가 아니라 host Fragment이다.



FragmentManager로 Fragment를 추가/삭제/교체를 도와주는 역할이  
FragmentTransaction 입니다.

FragmentTransaction?

각 사용자가 commit 하게되는 프래그먼트의 변경 사항의 집합을 transaction이라고 부르며, FragmentTransaction 클래스에 의해 제공되는 API를 사용하여 트랜잭션 내부에서 무엇을 할지 지정할 수 있다.


  • beginTransaction() 트랜잭션 인스턴스를 가져올수 있습니다.
supportFragmentManager.beginTransaction()

  • 트랜잭션으로 프래그먼트를 추가하는 예제이고 반드시 commit() 호출 해야 합니다.
supportFragmentManager
     .beginTransaction()
     .add(ExampleFragment(), "tag")
     .commit()

  • androidx.fragment:fragment-ktx dependancy를 추가했다면 commit 자동으로 해주는 확장함수도 지원하고 있습니다.
supportFragmentManager.commit {
	add(ExampleFragment(), "tag")
}

  • commit에는 4가지 메소드를 제공하고 있습니다.
commit()
commitAllowingStateLoss()
commitNow()
commitNowAllowingStateLoss()

기본적으로 commit() 사용을 권장하고 있고, commit()과 commitNow()의 차이점은 commit()은 트랜잭션의 커밋을 예약하고 즉시 실행되지 않습니다. 메인스레드의 작업 스케줄로 추가 되며, commitNow()는 즉시 실행되는 동기 방식입니다.

이전에는 commit()을 한 후 executePendingTransactions()를 불러 트랜잭션 
실행을 기다리는 방법이 있었지만, 이 방법은 자신과는 관계없는 모든 트랜잭션에 대해서도 
그 자리에서 실행해 버리기 때문에 그 점에서 자신만 즉시 실행하는 commitNow() 쪽이 
낫다는 것입니다. 따라서 commitNow()는 사용자가 실제 실행하고자 하는 것보다 더 많은 
트랜잭션을 실수로 실행할 수 없도록 도와줍니다.

❗️주의할 점은 백스택에 추가하는 addToBackStack()을 commitNow()에서 사용할 수 없다는 것 입니다. 그 이유는 commit()으로 비동기로 백스택에 트랜잭션을 추가하고 commitNow()로 즉각적으로 백스택에 추가한다면 백스택에 순서 보장이 안되기 때문입니다.

commit 과 commitAllowingStateLoss 는 구현에 있어서 거의 비슷하다고 볼 수 있습니다. 유일한 차이는 commit이 FragmentManager 가 자신의 state 를 저장했는지 확인한다는 것입니다. 만약 이미 state 를 저장했다면 IllegalStateException 를 던집니다.

그렇다면 commitAllowingStateLoss 는 어떤 state 를 잃는(loss) 다는 것일까요? 정답은 FragmentManager 의 state 그리고 onSaveInstanceState 이후에 추가, 제거된 Fragment들의 state 를 잃을 수 있다는 것입니다.


요약하면 onSaveInstanceState 이후 commit()을 하게되면 트랜잭션 상태를 잃어버려 사용자 입장에서 원하는 동작이 안될수 있습니다.


위 표를 보시면 안드로이드 버전 3.0 허니콤 이후 lifecycle에 변경이 있었고, onStop() 정확히는 super.onStop() 이후 commit()을 호출하면 Exception이 발생합니다. onPause와 onStop의 사이 또는 onPause이전에 호출되어야 합니다. 그래서 state를 잃어버려도 괜찮은 경우 Exception을 피하고 싶을때 commitAllowingStateLoss()을 사용할 수 있습니다.

만약 API를 호출하거나 비동기 작업을 호출하고 그에 callback으로 commit()을 수행한다고 하면 작업 도중 홈키로 앱이 백그라운드 상태에 간다면 Exception이 발생하게 됩니다.


마지막으로 FragmentManager로 Activity에 포함된 프래그먼트들 또는 Fragment내에 있는 Fragment를 찾는 2가지 방법이 있습니다.

supportFragmentManager.findFragmentById(R.id.container1)
supportFragmentManager.findFragmentByTag("tag")



참고 사이트

0개의 댓글