After several years of applying Fragment to Android app development, I have to say that although Fragment's concept is excellent, it also brings a lot of problems. When we are dealing with the state of the instance, we need to repair it in a special way.
First, although there is an Activity-like onSaveInstanceState approach, it clearly does not cover all situations. In other words, you can't just rely on onSaveInstanceState methods to save/restore the state of a view. Here are some case studies.
Case 1: When there is only one Fragment in the stack, rotate the screen
Case 1
Screen rotation is the simplest case for testing the save/restore of instance state. This situation is easy to handle, you simply need to save something, such as: member variables, it will also be lost in the onsaveinstancestate screen rotation, in onactivitycreated or onviewstaterestored recovery, As shown below:
int somevar; @Override protected void onsaveinstancestate (Bundle outstate) { outstate.putint ("Somevar", Somevar); Outstate.putstring ("Text", Tv1.gettext (). toString ());} @Overridepublicvoid onactivitycreated (Bundle savedinstancestate) { Super. onactivitycreated (savedinstancestate); = Savedinstancestate.getint ("Somevar", 0); Tv1.settext (savedinstancestate.getstring ("text"));}
Does it look good? But it's not all that works. This situation is not onsaveinstancestate in the callback, but the view is regenerated. What does that mean? Everything in the UI is gone. Here's the case.
Case 2: Fragment in the back stack
Case 2
When a fragment is returned from the back stack (fragment A is in this case), the fragment life cycle that fragment a view will follow is re-created.
Fragment life cycle
You will see that when fragment returns from the back stack, the Ondestroyview method and the Oncreateview method are recalled. Anyway, obviously in this case the Onsaveinstancestate method is not called. The result is that everything in the UI is gone, and then recreated by default as defined in the Layout XML file.
In any case, a view that implements the preservation of the intrinsic view state, such as EditText or TextView with Android:freeezetext, can still preserve the state of the view because Fragment implements the state of the inner view. But we developers can't catch these events. The only thing we can do is to manually save the instance state in the Ondestroyview method.
@Override Public void onsaveinstancestate (Bundle outstate) { super. Onsaveinstancestate (outstate); // Save State here } @Overridepublicvoid Ondestroyview () { super. Ondestroyview (); // SaveState here}
The problem also comes, Ondestroyview does not provide any help to save the instance state to a Bundle, where should we save these instance states to? The answer is Argument, and it will remain with Fragment.
Now the code looks like this:
Bundle savedstate; @Override Public voidonactivitycreated (Bundle savedinstancestate) {Super. onactivitycreated (savedinstancestate); //Restore State here if(!restorestatefromarguments ()) { //First time running, Initialize something here}} @Override Public voidonsaveinstancestate (Bundle outstate) {Super. Onsaveinstancestate (outstate); //Save State heresavestatetoarguments ();} @Override Public voidOndestroyview () {Super. Ondestroyview (); //Save State heresavestatetoarguments ();}Private voidsavestatetoarguments () {savedstate=saveState (); if(Savedstate! =NULL) {Bundle b=getarguments (); B.putbundle ("internalSavedViewState8954201239547", savedstate); }}Private Booleanrestorestatefromarguments () {Bundle B=getarguments (); Savedstate=b.getbundle ("internalSavedViewState8954201239547"); if(Savedstate! =NULL) {restorestate (); return true; } return false;}///////////////////////////////////Restore Instance State here/////////////////////////////////Private voidrestorestate () {if(Savedstate! =NULL) { //For Example//Tv1.settext (savedstate.getstring ("text")); }}////////////////////////////////Save Instance State here//////////////////////////////PrivateBundle SaveState () {bundle state=NewBundle (); //For Example//state.putstring ("text", Tv1.gettext (). toString ()); returnState ;}
You can easily saveState save your fragment state in the restoreState recovery state. Now it's looking a lot better. We're almost over, but there's a weird situation.
Case 3: Rotate the screen two times when more than one fragment is in the back stack
Case 3
When you rotate the screen once, the onsaveinstancestate will be called back, as we expect, the state of the UI will be saved. But when you rotate the screen more than once, the above code can cause the app to crash. The reason is that although the Onsaveinstancestate method is called when you rotate the screen, the fragment in the back stack completely destroys the view until you return to the original fragment to recreate it. So, once you rotate the screen again, there is no view to save the state. When you try to access a view that does not exist, saveState() it will cause nullpointerexception, causing the app to crash.
The method is to check whether the view exists in fragment. If it exists, save it, and if it does not, then savedstate it in Argument, and then save it when it returns. Or we don't even have to do anything, because it's done in argument.
Private void savestatetoarguments () { ifnull) = saveState (); if NULL ) { = getarguments (); B.putbundle ("savedstate", savedstate); }}
Ha, it's all settled now!
Fragment Final Template
The following is the fragment template I am now using for my work.
ImportAndroid.os.Bundle;Importandroid.support.v4.app.Fragment;ImportAndroid.view.LayoutInflater;ImportAndroid.view.View;ImportAndroid.view.ViewGroup;ImportCOM.INTHECHEESEFACTORY.THECHEESELIBRARY.R;/***created by Nuuneoi on 11/16/2014.*/ Public classStatedfragmentextendsFragment {Bundle savedstate; Publicstatedfragment () {Super(); } @Override Public voidonactivitycreated (Bundle savedinstancestate) {Super. onactivitycreated (savedinstancestate); //Restore State here if(!restorestatefromarguments ()) { //First time , Initialize something hereonfirsttimelaunched (); } } protected voidonfirsttimelaunched () {} @Override Public voidonsaveinstancestate (Bundle outstate) {Super. Onsaveinstancestate (outstate); //Save State heresavestatetoarguments (); } @Override Public voidOndestroyview () {Super. Ondestroyview (); //Save State heresavestatetoarguments (); } //////////////////// //Don ' t Touch ! //////////////////// Private voidsavestatetoarguments () {if(GetView ()! =NULL) savedstate=saveState (); if(Savedstate! =NULL) {Bundle b=getarguments (); B.putbundle ("internalSavedViewState8954201239547", savedstate); } } //////////////////// //Don ' t Touch ! //////////////////// Private Booleanrestorestatefromarguments () {Bundle B=getarguments (); Savedstate= B.getbundle ("internalSavedViewState8954201239547"); if(Savedstate! =NULL) {restorestate (); return true; } return false; } ///////////////////////////////// //Restore Instance State here ///////////////////////////////// Private voidrestorestate () {if(Savedstate! =NULL) { //For Example//Tv1.settext (savedstate.getstring ("text"));onrestorestate (savedstate); } } protected voidonrestorestate (Bundle savedinstancestate) {}////////////////////////////// //Save Instance State here ////////////////////////////// PrivateBundle SaveState () {bundle state=NewBundle (); //For Example//state.putstring ("text", Tv1.gettext (). toString ());onsavestate (state); returnState ; } protected voidonsavestate (Bundle outstate) {}}
If you use this template, simply inherit the class StatedFragment , then onSaveState() save things in, and onRestoreState() restore them in. The above code will do the rest of the work for you, I believe this has covered the possible circumstances I have known.
Setretaininstance can help developers handle member variables when layout changes (e.g. screen rotation) and you may notice that I'm not setting setretaioninstance to true. Please keep in mind that this is my purpose, because it setRetainInstance(true) does not cover the whole situation. The main reason is that you can't save the nested fragment that you often use back and on again and again. So I suggest that you do not save the instance unless you are 100% sure that the fragment will not be used for nesting.
Usage
Good news. The blog Statefragment now joins a very easy-to-use library that has now been published on Jcenter. Now you can simply add a dependency to your project's Build.gradle file. As shown below:
dependencies { compile ' com.inthecheesefactory.thecheeselibrary:stated-fragment-support-v4:0.9.1 '}
Inherit the statefragment, and then save the state in, in the onSaveState(Bundle outState) onRestoreState(Bundle saveInstanceState) restore state. If you want to do something on the first boot of the fragment, you can also overwrite the onFirstTimeLaunched() method (which will not be called later).
Public classMainfragmentextendsstatedfragment {.../*** Save Fragment ' s state here*/@Overrideprotected voidonsavestate (Bundle outstate) {Super. Onsavestate (outstate); //For Example://outstate.putstring ("text", Tvsample.gettext (). toString ()); } /*** Restore Fragment ' s state here*/@Overrideprotected voidonrestorestate (Bundle savedinstancestate) {Super. Onrestorestate (savedinstancestate); //For Example://Tvsample.settext (savedinstancestate.getstring ("text")); } ...}
Any comments and suggestions are welcome!
This is my spare time translation, but also my first use of markdown writing, a lot of mistakes, please also patience to point out, thank you!
For the original link, please click the original link
Save and restore the status of Android Fragment