According to the documentation of onSaveInstanceState:
The default implementation takes care of most of the UI per-instance state
and onRestoreInstanceState:
The default implementation of this method performs a restore of any view state that had previously been frozen
I'm not sure exactly what that means.
Is it meant to mean that when returning after being killed and now restored, that the UI screen shown to the user is automatically restored with all its data?
If so, I am not seeing that.
All I get is an empty screen unless I do setContentView myself.
AM I misunderstanding the meaning?
Default implementation will work for every widget which ids are defined.
For example, If you have one EditText and if you will provide its id then system will save its value when Activity will be killed due to orientation and same and it will restore the EditText value when activity will be re-created.
Edit
If you have one base layout and if you are dynamically adding some views in the view hierarchy then you will have to handle the save state and restore state your self. also when your activity will be re-created then onCreate() method of the activity will be called so in this method first set all the addition views which you are creating and adding dynamically and then you can check the extra parameters with the intent which you are getting in the onCreate() method. This extra parameters are exactly same as you have adding extra parameters in the onSaveInstanceState method.
So implement like below.
int x = 10;
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("x", x);
}
And in onCreate method you can get this x parameters like below
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
if(savedInstanceState.containsKey("x")) {
x = savedInstanceState.getInt("x");
}
}
Related
I've got an Android application which maintains state regarding distance traveled, time elapsed, etc. This state I can conveniently store in an object and store a reference to that object in the Bundle when Android calls onDestroy() when the user changes the screen orientation, then restore the state in onCreate(Bundle savedBundle). However, I also have some state in the Buttons and EditText objects on the screen that I want to persist through screen orientations. For example, in onStart(Bundle savedBundle) I call:
_timerButton.setBackgroundColor(Color.GREEN);
_pauseButton.setBackgroundColor(Color.YELLOW);
_pauseButton.setEnabled(false);
Then throughout the operation of my app, the colors/enabled status of these buttons will be changed. Is there a more convenient way to persist the state of user interface items (EditText, Button objects, etc) without having to manually save/restore each attribute for each button? It feels really clumsy to have to manually manage this type of state in between screen orientations.
Thanks for any help.
Add android:configChanges in the Manifest file
<activity name= ".MainActivity"
android:configChanges="orientation|screenSize"/>
By default, this does not work because changing the orientation causes the onCreate method to be called again and redraws the view.
However, if this parameter is included, the framework will handle preserving the state of the screen or layout if the orientation is changed.
Refer following official documentation for more info:
Activity Lifecycle
Handling configuration changes
To save your variable or values you should use onSaveInstanceState(Bundle); and when orientation changes then should recover values should use onRestoreInstanceState() as well, but not very common. (onRestoreInstanceState() is called after onStart(), whereas onCreate() is called before onStart().
Use the put methods to store values in onSaveInstanceState()
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
icicle.putLong("param", value);
}
And restore the values in onCreate():
public void onCreate(Bundle icicle) {
if (icicle != null){
value = icicle.getLong("param");
}
}
I have gone through the activity life cycle document and as per document onSaveInstanceState() and onRestoreInstanceState() will be used to preserve the UI state.
To test the usage of the above methods:
Case 1: I have create a simple layout with edit box and toggle button and I have entered some text in my edit text field and changed toggle button to 'on' and then changed the orientation of the activity. To my surprise my activity able to retain the values without saving state in onSaveInstanceState() method.
Case 2: Navigated to other activity and came back to my activity, in this case also its retaining its value.
So when activity able to retain its state then what the purpose of below methods.
onSaveInstanceState()
onRestoreInstanceState()
The most common usage of these functions are when your app is killed in the background by the android OS to allocate memory space for other applications.
When the user comes back to your application you would need to restore the last shown views/values to the user. This is done via onSaveInstanceState & onRestoreInstanceState.
#Override
public void onSaveInstanceState(Bundle outState)
{
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
// Save the values in a bundle which you would like to restore
outState.putString("vals", val1);
};
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
// restore your values here
val1 = savedInstanceState.getString("vals");
}
You can watch this video about restoration - https://www.youtube.com/watch?v=ekN2zvFytZk.
But in a nutshell android can restore view's state by traversing all views in the hierarchy and get their values(in your case EditText value). And one important thing - views have to have set ids.
These methods can be useful when you want to store variables in your activity. For example - you're implementing book reader and you might want to save view_mode(night_mode/day_mode) that user selected.
The system has a default behavior; it save the state of views that has an ID, this feature is not guaranteed, and in some cases, you must override this method and save the state of your views.
FROM DOC:
"The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself."
I am really confused with the internal state of a Fragment.
I have an Activity holding only one Fragment at once and replaces it, if another Fragment should get shown. From the docs onSaveInstanceState is called ONLY if the Activitys onSaveInstanceState is getting called (which isn't called in my case).
If I stop my Fragment, I'll store its state myself inside a Singleton (yeah, I know I hate Singletons, too, but wasn't my idea to do so).
So I have to recreate the whole ViewHirarchy, create new Views (by using the keyword new), restore its state and return them in onCreateView.
I also have a Checkbox inside this View from which I explicitly do NOT want to store its state.
However the FragmentManager wants to be "intelligent" and calls onViewStateRestored with a Bundle I never created myself, and "restores" the state of the old CheckBox and applies it to my NEW CheckBox. This throws up so many questions:
Can I control the bundle from onViewStateRestored?
How does the FragmentManager take the state of a (probably garbage-collected) CheckBox and applies it to the new one?
Why does it only save the state of the Checkbox (Not of TextViews??)
So to sum it up: How does onViewStateRestored work?
Note I'm using Fragmentv4, so no API > 17 required for onViewStateRestored
Well, sometimes fragments can get a little confusing, but after a while you will get used to them, and learn that they are your friends after all.
If on the onCreate() method of your fragment, you do: setRetainInstance(true); The visible state of your views will be kept, otherwise it won't.
Suppose a fragment called "f" of class F, its lifecycle would go like this:
- When instantiating/attaching/showing it, those are the f's methods that are called, in this order:
F.newInstance();
F();
F.onCreate();
F.onCreateView();
F.onViewStateRestored;
F.onResume();
At this point, your fragment will be visible on the screen.
Assume, that the device is rotated, therefore, the fragment information must be preserved, this is the flow of events triggered by the rotation:
F.onSaveInstanceState(); //save your info, before the fragment is destroyed, HERE YOU CAN CONTROL THE SAVED BUNDLE, CHECK EXAMPLE BELLOW.
F.onDestroyView(); //destroy any extra allocations your have made
//here starts f's restore process
F.onCreateView(); //f's view will be recreated
F.onViewStateRestored(); //load your info and restore the state of f's view
F.onResume(); //this method is called when your fragment is restoring its focus, sometimes you will need to insert some code here.
//store the information using the correct types, according to your variables.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("foo", this.foo);
outState.putBoolean("bar", true);
}
#Override
public void onViewStateRestored(Bundle inState) {
super.onViewStateRestored(inState);
if(inState!=null) {
if (inState.getBoolean("bar", false)) {
this.foo = (ArrayList<HashMap<String, Double>>) inState.getSerializable("foo");
}
}
}
I am inflating a view on button click and the user can add as many views as he likes, all is fine I made it work, but now the problem is when I go back one activity and come again to my dynamically generated activity every single view that was generated is gone. Similar is the case if I go to next activity and come back to the inflated activity. I know about onSaveInstance and onRestoreSaveInstance. But how do I put view information in a bundle in onSaveInstanceState? Please note that my view was generated Dynamically i.e. on button Click and I want to know as of how to preserve the state of my activity.
How do you go about it?
I am thinking that you should implement some kind of logic that helps you restore the state of your Views. So you should be designing a class, let say ViewDetail that somehow keeps details about the Views that you are adding.... type, dimension, etc. This class should implement Parcelable so you are able to add it to the bundle.
So you will keep an ArrayList<ViewDetail>, myViews where everytime the user adds a new View you create a new ViewDetail object that you add to your myViews array.
And then save your Views and restore them using those objects:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save your view states
outState.putParcelableArrayList("MY_VIEWS",myViews);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//get the views back...
myViews=savedInstanceState.getParcelableArrayList("MY_VIEWS");
//TODO: add the views back to your Activity
}
As your application may be killed completely at any moment without noticem you have to provide long term storage off heap memory
You only have to restore all the views, if your activity was terminated (and it can be at any time). When it is activated again after termination, it goes through onCreate() method
- this would be proper place to restore activity state.
Only callback which is guaranted to be called before your application / activity is destroyed is onPause() - this is a proper place to save views states into long term off-heap storage.
I make a login form with dynamic field validation. I have 3 fields username, email & password & all of these field are required. When field length = 0, I set error
editText.setError( getText(R.string.cannot_be_blank) );
and this code works fine, but when I change the orientation, all the errors disappear
How to save error state?
Thanks.
When the orientation is changed the framework will recreate the Activity by calling onCreate(Bundle savedInstanceState). Before the switch in orientation the onSaveInstanceState(Bundle outState) method will be called if it is overridden in your Activity.
You can save the state of your errors in the Bundle passed into the onSaveInstanceState method. This bundle is passed to your onCreate() method as the savedInstanceState Bundle.
Therefore you need to override the onSaveInstanceState method in your Activity as follows (saving the state of your errors):
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("errorOccurred", errorState);
super.onSaveInstanceState(outState);
}
Then in your onCreate method check if the savedInstateState Bundle is null or not. If not, you can retrieve the values out of it with the following code:
boolean errorOccurred = false;
if (savedInstanceState != null) {
errorOccurred = savedInstanceState.getBoolean("errorOccurred");
}
When the orientation is changed the Android framework destroys the Activity and then creates a new one for the new orientation. So all your state is lost.
Use SharedPreferences to store and restore your state and TextEdit values.
What happens when you turn the device is that your Activity runs through its lifecycle in order to deal with the fact that the layout must change from portrait to landscape.
You should take a look at the developer docs on how to Handle Runtime Changes.