Search code examples
androidandroid-fragmentsscreen-rotationsavestateandroid-nested-fragment

Restore nested fragments on rotation


I have an activity with fragment. A spinner in the fragment determines which of 2 fragments appears below the spinner. These 2 fragments inherit an abstract fragment. In the fragments, the user enters some data in fields. I need to restore the data after screen rotation. How to do that using onSaveInstanceState? I don't want to retain fragments or something else.

Code

  • In the activity I don't do anything related to rotation or saving the state. I don't override onSaveInstanceState.
  • In the fragment with the spinner

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(SPINNER_SELECTION, selectedPosition);
        outState.putParcelable(FIELDS, getFragmentManager()
            .saveFragmentInstanceState(getFragmentManager()
            .findFragmentById(R.id.container_with_fields)));
    }
    

    In onCreateView I check if (savedInstanceState == null) and restore the fields.

  • In the inner fragments I don't do anything.

Error

Currently the onSaveInstanceState method is invoked but not the onCreateView method. Somewhere in between I get the following error which seems to have nothing to do with my code:

07-03 10:33:06.079: W/dalvikvm(4286): threadid=1: thread exiting with uncaught exception (group=0x41e26700)
07-03 10:33:06.079: E/AndroidRuntime(4286): FATAL EXCEPTION: main
07-03 10:33:06.079: E/AndroidRuntime(4286): java.lang.NullPointerException
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1599)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1655)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.Activity.onSaveInstanceState(Activity.java:1238)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.Activity.performSaveInstanceState(Activity.java:1186)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1240)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3851)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.ActivityThread.access$800(ActivityThread.java:159)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1322)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.os.Looper.loop(Looper.java:176)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at android.app.ActivityThread.main(ActivityThread.java:5419)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at java.lang.reflect.Method.invokeNative(Native Method)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at java.lang.reflect.Method.invoke(Method.java:525)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
07-03 10:33:06.079: E/AndroidRuntime(4286):     at dalvik.system.NativeStart.main(Native Method)

The app does not crash but the activity seems to get finished. After the rotation, the screen freezes shortly and the parent activity is displayed.

I have checked that I have the latest SDKs and libs installed and used in the project.


Solution

  • I found out that the error appears because of this line:

    outState.putParcelable(FIELDS, getFragmentManager()
        .saveFragmentInstanceState(getFragmentManager()
        .findFragmentById(R.id.container_with_fields)));
    

    I still can't explain it...

    For some reason, I couldn't manage it to do the saving using onSaveInstanceState but I also read that it is much better to use setRetainInstance. While the former causes additional overhead, the latter skips some of the methods. So, I ended up retaining the instance of the outer fragment. Also, I added properties (member variables) for storing everything entered in the fragments' fields. In this way, I restore the state once the fragment gets recreated. Well, said correctly, I check whether my properties are initialized and if so, I put the values they have in the fields in the fragments.

    Hope this helps someone else.