프레그먼트를 사용하다보면, 아래와 같은 에러를 만날 수 있다.
Shutting down VM FATAL EXCEPTION: main Process: me.sunphiz.android.fragment, PID: 18882 java.lang.IllegalStateException: Failure saving state: CalleeFragment{68fee09 #7 CalleeFragment} has target not in fragment manager: CallerFragment{9c5480e} at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1843) at android.support.v4.app.FragmentController.saveAllState(FragmentController.java:134) at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:566) at android.support.v7.app.AppCompatActivity.onSaveInstanceState(AppCompatActivity.java:498) at android.app.Activity.performSaveInstanceState(Activity.java:1474) at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1317) at android.app.ActivityThread.callCallActivityOnSaveInstanceState(ActivityThread.java:5398) at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4700) at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4774) at android.app.ActivityThread.access$1400(ActivityThread.java:222) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1819) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:158) at android.app.ActivityThread.main(ActivityThread.java:7229) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
먼저 로그를 옮기자면, CalleeFragment가 프레그먼트에 없는 프레그먼트를 타겟(target)으로 가지고 있어, 상태를 저장할 수 없다는 뜻이다.
“Failure saving state”로그를 보자. CalleeFragment는 라이프사이클에 따라 onSaveInstanceState()가 호출되었다. onSaveInstanceState()는 프래그먼트가 포그라운드(foreground)에서 물러날 때, onPause() ~ onDestroy() 사이에 호출되는 메소드로, 개발자에게 프레그먼트의 상태나 데이터를 저장할 수 있는 기회를 주는 메소드다. 이 때, 문제가 생겨 위와 같은 에러가 발생했다.
“CalleeFragment{68fee09 #7 CalleeFragment} has target not in fragment manager: CallerFragment{9c5480e}”로그를 살펴보자. CalleeFragment의 setTargetFragment()를 호출할 때, 프레그먼트의 인스턴스를 건넸기 때문에 타겟 프레그먼트가 처음부터 생성되지 않았을리는 없다. 그보다는, FragmentManager.replace()나 remove() 메소드로 제거되었을 것이다.
이에 대한 해결책으로는 크게 두 가지가 있다. 하나는, CalleeFragment의 onSaveInstanceState()가 호출 될 때, 이 메소드 안에서 setTargetFragment( null, -1 ) 줄을 추가하여 타겟을 날려버리는 것이다. 다른 하나는, 프레그먼트의 흐름을 파악하여, 적절한 곳에서 remove(), replace() 등을 해주는 것이다.
앞에서 설명한 두 가지 방법 중 좋은 방법은 당연히 후자이다. onSaveInstanceState()는 프레그먼트의 라이프 사이클 중 일부로 당연히 호출되는 것이다. 이를, 감안하여 개발해야지 암의로 초기화 하는 미봉책으로 수정하는 것은 대부분의 경우 다른 사이드 이펙트를 발생시킨다. 예를 들어 targetFragment를 초기화 해버리면, getTargetFragment().onActivityResult()와 같은 메소드를 더이상 호출할 수 없게 된다.
참조