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."
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 FragmentA hosted by ActivityA. When the user selects an item from the options menu, ActivityB is started which hosts FragmentB. For now, I want to retain a String and a boolean from FragmentA by overriding onSaveInstanceState(), so when the user returns to FragmentA, their information is preserved.
Code from FragmentA:
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
//LOGS SHOW THAT THIS IS ALWAYS CALLED WITH CORRECT VALUES
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("string", "example");
savedInstanceState.putBoolean("boolean", bool);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragmentA, parent, false);
//LOGS SHOW THAT THIS IS ALWAYS NULL
if (savedInstanceState != null)
{
if (savedInstanceState.getString("text") != null)
{
mObject.setText(savedInstanceState.getString("string"));
}
bool = savedInstanceState.getBoolean("boolean");
}
...
}
From reading previous problems similar to mine:
1) I decided to place the code to recover the information in onCreateView() because onCreate() will not always be called. (Although tests with the code in onCreate() also have the same problem.)
2) I also did not call setRetainInstance(true), since this will cause Bundle savedInstanceState to always be null.
3) I made sure that the XML layout for FragmentA has an id. The various children elements of this layout also have ids.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
...
</LinearLayout>
Despite this, Bundle savedInstanceState is always null. What am I missing here?
Any help appreciated. Thanks!
Use the onPause() to save your persistent data, that's referring to data that you would like to keep permanently, and hence, you save it in your SharedPreferences or your Database. onSaveInstanceState on the other hand retrains the data in case an activity is destroyed and you'd like to get that data back, a good scenario would be a user filling in a form. You said in your example that you're navigating from Activity A to Activity B, then you go back to Activity A, when you first navigate, Activity A is not destroyed, it's only sent to the background, so when you return to it, it's already there and will be brought to the foreground, your values should actually be there unchanged, and onCreate and onCreateView will not be called as the Activity is still alive (although it might be killed in case of low memory on device).
Best and fastest way to test your onSaveInstanceState is the most often used scenario, Orientation Change, orientation change will cause the activity to get destroyed completely and re-created, so allow orientation change on Activity A, put some values in your saveStateBundle and rotate the device, now this will call all your methods from the start, onCreate, onCreateView, ... etc to create the activity with the appropriate layout, your savedInstanceState should not be null now.
Note this is assuming your application is staying alive, if you're going to close the app completely and still want to keep the data, then put your information in the SharedPreferences or Database and retrieve them when you start the app again.
// Edit 1
to show you how to store values in SharedPreferences anywhere in your application, these values are persistent even if your application is closed. (although onSaveInstanceState should be enough to what you're looking for but hope this helps)
// SharedPreference
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
// Storing
preferences.edit().putString("valueNameOrKey", StringValue).commit();
// Retrieving
preferences.getString("valueNameOrKey", defaultValueToReturnInCaseThatKeyIsNotFound);
// Edit 2
To remove any key/value pair from the SharedPreferences you can do this:
preferences.edit().remove("valueNameOrKey").commit();
But then pay attention to what happens when you retrieve the value, since the key will not be available, it's going to return the default value instead like this:
preferences.getString("valueNameoOrKey", ""); // "" Is my default value since I'm using a String
since you can use putString, putInt, putBoolean etc, same goes for the get functions, your default value must match the expected return type.
The savedInstanceState is null when no data was been previously saved. To save data you must override the onSaveInstanceStateBundle(Bundle) method as described in the Android documentation:
you should use the onPause() method to write any persistent data (such as user edits) to storage. In addition, the method onSaveInstanceState(Bundle) is called before placing the activity in such a background state, allowing you to save away any dynamic instance state in your activity into the given Bundle, to be later received in onCreate(Bundle) if the activity needs to be re-created. See the Process Lifecycle section for more information on how the lifecycle of a process is tied to the activities it is hosting. Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
More info here
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");
}
}
}
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");
}
}
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.