The best way to save and restore the Fragment state in Android is androidfragment.
Probably be the best way (?) To save/restore Android Fragment's state so far
Key Point: Arguments of Fragment.
After using Fragment over the past few years, I would like to say that Fragment is indeed a smart design, but there are too many problems that need to be solved one by one when using Fragment, especially when processing data persistence.
First, although it has an onSaveInstanceState similar to the activity, do not rely on onSaveInstanceState to maintain data.
The following are some cases:
Scenario 1: rotate the screen when there is only one Fragment in the stack.
Yes, rotating the screen is the easiest way to keep test data. This situation is very simple. You only need to store the data that will be lost during rotation in the onSaveInstanceState storage, including the variables, and then retrieve the data in onActivityCreated or onViewStateRestored:
int someVar;@Overrideprotected void onSaveInstanceState(Bundle outState) { outState.putInt("someVar", someVar); outState.putString(“text”, tv1.getText().toString());}@Overridepublic void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); someVar = savedInstanceState.getInt("someVar", 0); tv1.setText(savedInstanceState.getString(“text”));}
It looks very simple, but there is such a situation that the View is rebuilt, but the onSaveInstanceState is not called, which means that everything on the UI is lost. Please refer to the following case.
Scenario 2:FragmentReturn from rollback Stack
When fragment is returned from the backstack (Fragment A here), the view in Fragment A is rebuilt according to the description of the Fragment lifecycle in the official document.
From this figure, we can see that when Fragment returns from the rollback stack,OnDestroyView and onCreateViewCalled,OnSaveInstanceStateIt seems that the interface has not been called, which leads to the returning of all UI data to the initial state defined in the xml layout. Of course, those views that implement state saving internally, such as EditText and TextView with the android: freezeText attribute, can still be maintained because Fragment can keep data for them, however, developers cannot obtain these events. We can only manually Save the data in onDestroyView.
The general process is as follows:
@ Overridepublic void onSaveInstanceState (Bundle outState) {super. onSaveInstanceState (outState); // save data here} @ Overridepublic void onDestroyView () {super. onDestroyView (); // If onSaveInstanceState is not called, data can be saved here}
But the question is, it is very easy to save data in onSaveInstanceState because it has the Bundle parameter, but onDestroyView does not. Where can it be saved? The answer is that it can coexist with Fragment.Argument.
The code is roughly as follows:
Bundle savedState; @ Overridepublic void onActivityCreated (Bundle savedInstanceState) {super. onActivityCreated (savedInstanceState); // Restore State Here if (! RestoreStateFromArguments () {// First Time running, Initialize something here }}@ Overridepublic void onSaveInstanceState (Bundle outState) {super. onSaveInstanceState (outState); // Save State Here saveStateToArguments () ;}@ Overridepublic void onDestroyView () {super. onDestroyView (); // Save State Here saveStateToArguments ();} private void saveStateToArguments () {savedState = saveState (); if (savedS Tate! = Null) {Bundle B = getArguments (); B. putBundle ("internalSavedViewState8954201239547", savedState) ;}} private boolean restoreStateFromArguments () {Bundle B = getArguments (); savedState = B. getBundle ("internalSavedViewState8954201239547"); if (savedState! = Null) {restoreState (); return true;} return false ;} /// // retrieves status data // /// // private void restoreState () {if (savedState! = Null) {// For example, // tv1.setText (savedState. getString ("text "));}} /// // save status data ///// /// // private Bundle saveState () {Bundle state = new Bundle (); // For example, // state. putString ("text", tv1.getText (). toString (); return state ;}
Now you can easilySaveState andrestoreState
Data is stored and retrieved separately. Now it looks much better, almost succeeded, but there are more extreme cases.
Scenario 3: rotate more than one Fragment in the rollback stack twice
When you rotate the screen once, the onSaveInstanceState is called, and the UI status is saved as expected. However, when you rotate the screen again, the above Code may crash. The reason is that although onSaveInstanceState is called, When you rotate the screen, the Fragment view in the rollback stack will be destroyed and will not be rebuilt before the return. This leads to no view that can save data when you rotate the screen again.saveState()
It will reference a non-existent view, leading to a null pointer exception NullPointerException. Therefore, you must first check whether the view exists. If the status data is saved, save the data in the Argument again, or simply do nothing, because it has been saved for the first time.
private void saveStateToArguments() { if (getView() != null) savedState = saveState(); if (savedState != null) { Bundle b = getArguments(); b.putBundle(“savedState”, savedState); }}
Final Fragment template:
The following is the fragment template I am using.
import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup; import com.inthecheesefactory.thecheeselibrary.R; /** * Created by nuuneoi on 11/16/2014. */public class StatedFragment extends Fragment { Bundle savedState; public StatedFragment() { super(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Restore State Here if (!restoreStateFromArguments()) { // First Time, Initialize something here onFirstTimeLaunched(); } } protected void onFirstTimeLaunched() { } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save State Here saveStateToArguments(); } @Override public void onDestroyView() { super.onDestroyView(); // Save State Here saveStateToArguments(); } //////////////////// // Don't Touch !! //////////////////// private void saveStateToArguments() { if (getView() != null) savedState = saveState(); if (savedState != null) { Bundle b = getArguments(); b.putBundle("internalSavedViewState8954201239547", savedState); } } //////////////////// // Don't Touch !! //////////////////// private boolean restoreStateFromArguments() { Bundle b = getArguments(); savedState = b.getBundle("internalSavedViewState8954201239547"); if (savedState != null) { restoreState(); return true; } return false; } ///////////////////////////////// // Restore Instance State Here ///////////////////////////////// private void restoreState() { if (savedState != null) { // For Example //tv1.setText(savedState.getString("text")); onRestoreState(savedState); } } protected void onRestoreState(Bundle savedInstanceState) { } ////////////////////////////// // Save Instance State Here ////////////////////////////// private Bundle saveState() { Bundle state = new Bundle(); // For Example //state.putString("text", tv1.getText().toString()); onSaveState(state); return state; } protected void onSaveState(Bundle outState) { }}
If you use this template, you only need to inheritStatedFragment
Class and thenonSaveState()
Save data,Retrieve Data in onRestoreState (), and the code above is ready for you. I believe it covers all the situations I know.
Library
Now the StatedFragment described in this article has been made into an easy-to-use library and released to the jcenter. Now you only need to add dependencies in build. gradle:
dependencies { compile 'com.inthecheesefactory.thecheeselibrary:stated-fragment-support-v4:0.9.1'}
Inherits StatedFragment andonSaveState(Bundle outState)
AndonRestoreState(Bundle savedInstanceState)
Save and retrieve status data. If you want to do something when fragment is started for the first time, you can also rewrite it.OnFirstTimeLaunched (), which is called only at the first startup.
public class MainFragment extends StatedFragment { ... /** * Save Fragment's State here */ @Override protected void onSaveState(Bundle outState) { super.onSaveState(outState); // For example: //outState.putString("text", tvSample.getText().toString()); } /** * Restore Fragment's State here */ @Override protected void onRestoreState(Bundle savedInstanceState) { super.onRestoreState(savedInstanceState); // For example: //tvSample.setText(savedInstanceState.getString("text")); } ... }
First-ever address http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2648.html