0

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?

Piovezan
  • 3,215
  • 1
  • 28
  • 45
  • Are you initializing/reinitializing in `onResume`? I would expect that to handle this case, but I haven't tested. – TBridges42 Jul 20 '15 at 23:35
  • I don't think there's any special cases to be handled in `onResume` in this case. – Piovezan Jul 20 '15 at 23:38
  • I mean, can you reinitialize `List` in `onResume`? It should be called when the fragment returns to the foreground. – TBridges42 Jul 20 '15 at 23:39
  • I would rather not. Doing that doesn't sound very maintainable to me. If my app's initial fragment can be a number of different fragment classes, or if I decide to add other fragments without saving the transaction to the back stack, I'd have to make a number of reinitializations on `onResume`. – Piovezan Jul 20 '15 at 23:47
  • Wait. Sorry, I was thinking about the activity's `onResume`. I could do that on the fragments' `onResume`, but to my understanding the problem goes beyond that. The `fragment1` instance is being recreated (it was destroyed during the rotations) and doesn't have a reference to the original `List` data. – Piovezan Jul 20 '15 at 23:56

1 Answers1

1

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."

TBridges42
  • 1,849
  • 1
  • 19
  • 29
  • I figured out that adding the transaction to the back stack is not needed. If I keep the data in the fragment's arguments bundle it will be persisted across destructions and recreations (the documentation of `Fragment.getArguments()` actually mentions this). If you mention this specific solution in your answer I'll be glad to accept it as thanks for your effort. – Piovezan Jul 22 '15 at 12:26