Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly save instance state of Fragments in back stack?

I have found many instances of a similar question on SO but no answer unfortunately meets my requirements.

I have different layouts for portrait and landscape and I am using back stack, which both prevents me from using setRetainState() and tricks using configuration change routines.

I show certain information to the user in TextViews, which do not get saved in the default handler. When writing my application solely using Activities, the following worked well:

TextView vstup;  @Override public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.whatever);     vstup = (TextView)findViewById(R.id.whatever);     /* (...) */ }  @Override public void onSaveInstanceState(Bundle state) {     super.onSaveInstanceState(state);     state.putCharSequence(App.VSTUP, vstup.getText()); }  @Override public void onRestoreInstanceState(Bundle state) {     super.onRestoreInstanceState(state);     vstup.setText(state.getCharSequence(App.VSTUP)); } 

With Fragments, this works only in very specific situations. Specifically, what breaks horribly is replacing a fragment, putting it in the back stack and then rotating the screen while the new fragment is shown. From what I understood, the old fragment does not receive a call to onSaveInstanceState() when being replaced but stays somehow linked to the Activity and this method is called later when its View does not exist anymore, so looking for any of my TextViews results into a NullPointerException.

Also, I found that keeping the reference to my TextViews is not a good idea with Fragments, even if it was OK with Activity's. In that case, onSaveInstanceState() actually saves the state but the problem reappears if I rotate the screen twice when the fragment is hidden, as its onCreateView() does not get called in the new instance.

I thought of saving the state in onDestroyView() into some Bundle-type class member element (it's actually more data, not just one TextView) and saving that in onSaveInstanceState() but there are other drawbacks. Primarily, if the fragment is currently shown, the order of calling the two functions is reversed, so I'd need to account for two different situations. There must be a cleaner and correct solution!

like image 250
The Vee Avatar asked Mar 09 '13 17:03

The Vee


People also ask

How can I maintain fragment state when added to the back stack?

Various Android system operations can affect the state of your fragment. To ensure the user's state is saved, the Android framework automatically saves and restores the fragments and the back stack. Therefore, you need to ensure that any data in your fragment is saved and restored as well.

How do I save an instance state to save an activity state?

In order to save sates of UI, I override onSaveInstanceState(Bundle savedInstanceState) and save all the data of the UI in savedInstanceState Bundle. This method is called before onStop() in older versions of Android (till android 8.0) and can be called after onStop() for newer versions.

How do you save the state of fragment Kotlin?

The first key is that we can save our Fragments' state ourselves using FragmentManager. saveInstanceState(Fragment) . Before removing the Fragment, call this method to get a Bundle containing your Fragment's saved state!

What is fragment back stack?

Calling addToBackStack() commits the transaction to the back stack. The user can later reverse the transaction and bring back the previous fragment by pressing the Back button. If you added or removed multiple fragments within a single transaction, all of those operations are undone when the back stack is popped.


1 Answers

To correctly save the instance state of Fragment you should do the following:

1. In the fragment, save instance state by overriding onSaveInstanceState() and restore in onActivityCreated():

class MyFragment extends Fragment {      @Override     public void onActivityCreated(Bundle savedInstanceState) {         super.onActivityCreated(savedInstanceState);         ...         if (savedInstanceState != null) {             //Restore the fragment's state here         }     }     ...     @Override     public void onSaveInstanceState(Bundle outState) {         super.onSaveInstanceState(outState);                  //Save the fragment's state here     }  } 

2. And important point, in the activity, you have to save the fragment's instance in onSaveInstanceState() and restore in onCreate().

class MyActivity extends Activity {      private MyFragment       public void onCreate(Bundle savedInstanceState) {         ...         if (savedInstanceState != null) {             //Restore the fragment's instance             mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");             ...         }         ...     }          @Override     protected void onSaveInstanceState(Bundle outState) {         super.onSaveInstanceState(outState);                      //Save the fragment's instance         getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);     }  } 
like image 104
ThanhHH Avatar answered Sep 22 '22 17:09

ThanhHH