컴퓨터공부/Android

Fragment 전환 시 IllegalStateException 원인 및 해결

achivenKakao 2022. 4. 30. 13:30

출처 : https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html

        https://proandroiddev.com/kotlin-extensions-to-commit-fragments-safely-de06218a1f4       

        https://stackoverflow.com/questions/7575921/illegalstateexception-can-not-perform-this-action-after-onsaveinstancestate-wit

 

원인

    Activity의 onSaveInstanceState()가 호출된 후에 FragmentTransaction의 commit()을 동작하는 데에 있습니다. 이건 안드로이드 플랫폼에서 개발자에게 경고로 주는 exception이다. 

설명 

Activity

      onSaveInstanceState()     <- 상태 A

      무언가 해서 상태가 바뀜        <- 상태 B

     commit()

위 Activity는 onSaveInstanceState() 후 commit()를 하였으며 그 사이에 상태 변화가 생겼다. 유저는 다른 화면으로 갔다가 다시 현재 Activity로 돌아왔을 때 상태 B를 기반으로한 화면을 예상하겠지만 Activity는 상태 B를 저장한 적이 없기에 상태 A를 기반으로 화면이 구성된다. 이것은 개발자와 유저 모두 원하는 결과가 아닐 것이다.  그래서 안드로이드 플랫폼은 이럴 때 exception을 발생해서 개발자에게 알려준다.

 

해결 방법

    정석적인 해결방법은 좀 애매하다,, Activity lifecycle을 잘 이해하고 써야 한다..

        onCreate()에서 commit()은 안전하다. FragmentActivity#onResume()에서는 stateRestord 를 할 수 있으니 FragmentActivity#onResumeFragments() or Activity#onPostResume()가 안전하다.

    async callback에서는 commit()하지 않는다.

    commitAllowingStateLoss()은 마지막 옵션이다. 이유는 간단하다. Activity의 상태 정보가 버려질 수 있다고 동의하는 commit이기 때문이다. 이 경우 Activity로 다시 돌아올 경우 이전 상태를 보장 할 수 없다.(물론 앱이 죽는것보다 낫지만,,,)

 

Case study

  1. popBackStackImmediate()를 사용할 때 IllegalStateException 가 발생
    1. java.lang.IllegalStateException: FragmentManager is already executing transactions 발생 
    2. popBackStackImmediate()는 즉시 작동하는 함수이다. 너무 많은 fragment가 한꺼 번에 닫으려고 할 때, 문제가 발생할 수 있다. popBackStack()를 이용하면 해결 할 수 있다.
    3. popBackStack()은 비동기적으로 작동하는 함수다.
      • 다음 loop cycle에서 pop이 수행됨.
      • 즉, FragmentTransaction이 수행된 후에는 백스택에서 팝되지 않음.
      • 물론 실제 사용 환경에서는 다음 loop cycle 역시 매우 빠르게 도달하기에, 사용자는 동작의 차이를 느끼기 힘듬.
    4. popBackStackImmediate()은 지금 즉시 작동하는 함수다.
      • 지금 즉시 수행하지만, 기기에 따라 성능적인 문제를 야기할 수 있음.
      • 타 함수와 연계되는 등, 정확한 타이밍에 반드시 일어나야 하는 때만 사용하기를 권장함.
      • 위와 같은 특별한 이유가 없을 때는 기본형인 popBackStack()를 사용하기를 권장.
    5. 출처 : https://danggai.github.io/android/popBackStack%EA%B3%BC-popBackStackImmediate%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90/
  2. 비동기 함수에서  popBackStackImmediate() 호출
    1. 일반적으로 하면 안되는 행동이다.
    2. java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 발생
    3. 비동기기 통신 후에 보통 popBackStackImmediate()가 호출 되므로 현재 예외가 좀 더 쉽게 발생할 수 있다. 통신 중에 fragment가 닫치는건 흔한 일이잖아?
    4. 비동기 함수에서  popBackStackImmediate() 호출하지 않는게 최선이지만, 어쩔 수 없을 경우 try-catch 구문으로 무시 하는 방법이 있다.