Android - Back Stack Fragment state loss after multiple screen orientation (onCreate vs onViewStateRestored)

     This post is applicable to activities with following criteria,

* Not handling screen orientation (android:configChanges="orientation")
* Hosting Fragments in Back stack via replace, with each Fragment responsible to load data over network/database and displaying in respective UI elements.
* Fragments saving their state in onSaveInstanceState and restoring it via onViewStateRestored

    The application having such activities usually move from one Fragment to the other based on user action like in this case,


    Both Fragments download their data set over network and load the data set into views. When the user in the second Fragment (Dance), the back stack has two fragments and they are managed by the Fragment Manager. Now, as and when the screen orientation changes, 

  ->  Fragment Manager invokes onSaveInstanceState() on both the fragments.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Fragment Manager invokes onViewStateRestored on newly created focus Fragment (Dance)

So far so good, now when the user presses back key in the new orientation,

 -> Fragment Manager invokes onViewStateRestored on newly created Fragment (Explore)

So everything seems to be fine and the application logic works. But what happens when the device's orientation is changed multiple times (more than once).

  -> First orientation
  ->  Fragment Manager invokes onSaveInstanceState() on both the fragments.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Fragment Manager invokes onViewStateRestored on newly created focus Fragment (Dance). Note that the fragments in back stack (Explore) are just created and their state isn't yet restored. Their members would basically be null at this point.
  -> Active fragment (Dance) seems to be fine.
  -> Second orientation
  ->  Fragment Manager again invokes onSaveInstanceState() on both the fragments. However, now the state of the fragments in the back stack is worthless as their state was never restored.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Active fragment (Dance) seems to be fine.
  -> User goes back to previous Fragment
  -> Fragment Manager invokes onViewStateRestored on newly created Fragment (Explore) with a bundle having no relevant data.
  -> Boom !!! onViewStateRestored doesn't have any valid data set to restore and it basically fails.

   Application developers would assume that their logic of saving and restoring state should either fail or work at all times. A selective failure doesn't really help to write generic code. So whats happening in the Framework?

   Turns out, Fragment Manager always tries to save state of all Fragments, irrespective of the state of the Fragment in the back stack. Why try saving an inactive Fragment in the back stack? Wouldn't its last known state be the most important one and be good enough? For now, there is a proposed fix to AOSP https://android-review.googlesource.com/#/c/112492/ and is awaiting feedback.

    Meanwhile, application developers can use onCreate() callback to restore state in the fragments. Fragment Manager at least invokes onCreate() for all fragments in the back stack. The bundle passed in onCreate is more reliable than the one passed in onViewStateRestored. This ensures each new instance of the Fragment having valid data set at all times. Nevertheless, this just helps manage data set state of the Fragment and not of the views which is managed in the base class via onViewStateRestored.

No comments: