Search code examples
androidandroid-fragmentsfragment-backstackfragmentmanager

How should I add my initial fragment while supporting screen rotations?


When my app starts I dinamically add a fragment instance (say, fragment1) to the content layout in a transaction which is not added to the back stack. This fragment displays some cached data which has been passed to it by means of a newInstance(List<Obj>) static method. In onSaveInstanceState() I save the data so I can display it if the fragment is recreated.

Now suppose I don't recreate the fragment. Suppose I replace it with a second fragment, say, fragment2 (adding the transaction to the back stack this time), perform two screen rotations, and press back. The app will pop the back stack and attempt to display fragment1 again which by its turn will attempt to display List<Obj> which will be null so a NullPointerException will be thrown.

I understand this is because the fragment1 instance has never been saved in the first place, since it wasn't in the back stack and neither was on display when the device rotated.

My question is, what's the most appropriate way of supporting screen rotations in this case? I could save the initial transaction in the back stack and make onBackPressed() verify that getSupportFragmentManager().getBackStackEntryCount() >= 1 before popping the back stack (I don't want the initial transaction to be popped because fragment1 is my initial screen) but I don't think this is the correct approach. Any ideas?


Solution

  • If your list is not very large, the thing to do would be to pass it to a bundle in onSaveInstanceState and retrieve it in onRestoreInstanceState. These methods should still be called on destruction/recreation even though the fragment is not in the foreground. However, if the data is very large or is not serializable or parcelable, this is not an option.

    From the docs:

    If restarting your activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive operations, then a full restart due to a configuration change might be a slow user experience. [...] In such a situation, you can alleviate the burden of reinitializing your activity by retaining a Fragment when your activity is restarted due to a configuration change. This fragment can contain references to stateful objects that you want to retain.

    It sounds like in this case you would want to retain the fragment that holds your List<Obj>. The problem is that this requires your activity to check all of the retained fragments it could create in onCreate to see if any of them already exist, and you've expressed concerns about maintainability in this instance.

    In the last resort, you could declare your intention to handle orientation changes yourself by using the android:configChanges parameter in your android manifest. This will prevent your activity (and its associated fragments and their members) from being destroyed and recreated in the orientation change, and instead the system will call onConfigurationChanged in your activity. If you do not override onConfigurationChanged, nothing will happen on orientation change except that the screen will rotate. Google discourages this, among other reasons, because it's a code smell. Resorting to this means that you are not handling state changes well, and if your activity was destroyed while a user was looking at something else, your state would not be preserved properly.

    Edit:

    If your fragment receives its data through its arguments bundle, as from a call to setArguments(), that bundle "will be retained across fragment destroy and creation."