Android View - What is automatically saved and restored in an Activity
Here is an example to explain you ...
- what is saved by default and how?
- what data would need you to add code in order for it to be saved?
I have created a simple android project which has total 4 data points that will have some value at some point of the time in the app's life cycle.
- Activity internal variable
saveMe
- Activity internal variable
saveMeNot
- View's EditText
withid
(has android:id) - View's EditText (has no android:id)
Here is the sequence of events as per the screenshots.
- Start the Android App
- Click on
SAVE
button to set the values for internal variablessaveMe
andsaveMeNot
. AToast
will be displayed that it saved the values for both the variables. - Type some text in both the edit text like Hello and Hi. This will set the text in both the edit text.
- Rotate the screen i.e. orientation change. Following will happen ....
- Android will save the values of all the views which have
android:id
defined in theactivity_main.xml
. Here only Hello will be saved as the EditText where Hello is typed has anandroid:id=@+id/withId
. Another EditText that has text Hi wouldn't be automatically saved by Android as it doesn't have anyandroid:id
. This is what you get for free (provided all your views haveandroid:id
defined). If you have Custom Views that extend View then they also haveandroid:id
defined. - Android also calls onSaveInstanceState and onRestoreInstanceState which gives you capability to store the state of all the internal variables of activity i.e.
saveMe
andsaveMeNot
. You would have to code for it else the state is lost. Like in my example, I have saved state ofsaveMe
and not forsaveMeNot
. This is something you do not get for free i.e. you would have to code for it.
- Android will save the values of all the views which have
- Click on
CLICK ME
button to view the values ofsaveMe
andsaveMeNot
and you will see onlysaveMe
values are displayed as it was saved by you inonSaveInstanceState
and retrieved inonRestoreInstanceState
Sequence of Steps
Code
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.saveinstance">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package test.saveinstance;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
// will save in bundle in onSaveInstanceState
private int saveMe;
// will not save in bundle in onSaveInstanceState
private int saveMeNot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// do some action that generates values for
// activity specific variables i.e. saveMe
// and saveMeNot
Button saveButton = (Button) findViewById(R.id.save);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
saveMe = 10;
saveMeNot = 20;
Toast.makeText(getApplicationContext(), "SAVED: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
}
});
// will be used to display value of
// saveMe and saveMeNot after orientation
// changes.
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "DISPLAY: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
}
});
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// save saveMe in bundle
outState.putInt("saveMe", saveMe);
super.onSaveInstanceState(outState);
Log.d("TEST", "Saving saveMe in bundle during orientation change");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// retrieve saveMe from bundle
saveMe = savedInstanceState.getInt("saveMe");
Log.d("TEST", "Retrieving saveMe in bundle during orientation change");
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="test.saveinstance.MainActivity">
<EditText
android:id="@+id/withId"
android:layout_marginTop="30dp"
android:layout_width="match_parent"
android:layout_height="70dp"
android:gravity="center"
android:hint="Type Here (has android:id)" />
<EditText
android:layout_below="@id/withId"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="70dp"
android:gravity="center"
android:hint="Type Here (doesn't have android:id)" />
<Button
android:id="@+id/button"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:text="Click Me"
android:layout_width="match_parent"
android:layout_height="50dp" />
<Button
android:id="@+id/save"
android:layout_above="@id/button"
android:layout_marginBottom="10dp"
android:text="Save"
android:layout_width="match_parent"
android:layout_height="50dp" />
</RelativeLayout>
Android Documentation that explains about saving states and a very good article on saving states in activity and fragment.
Android Documentation:
https://developer.android.com/guide/components/activities/activity-lifecycle.htmlAdditional Article:
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
Saving and restoring activity state There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling the
finish()
method. The system may also destroy the process containing your activity to recover memory if the activity is in the Stopped state and hasn't been used in a long time, or if the foreground activity requires more resources.
When your activity is destroyed because the user presses "Back" or the activity finishes itself, the system's concept of that
Activity
instance is gone forever because the behavior indicates the activity is no longer needed. However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actualActivity
instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the instance state and is a collection of key-value pairs stored in aBundle
object.
By default, the system uses the
Bundle
instance state to save information about eachView
object in your activity layout (such as the text value entered into an EditText widget). So, if your activity instance is destroyed and recreated, the state of the layout is restored to its previous state with no code required by you. However, your activity might have more state information that you'd like to restore, such as member variables that track the user's progress in the activity.
Save your activity state As your activity begins to stop, the system calls the
onSaveInstanceState()
method so your activity can save state information with a collection of key-value pairs. The default implementation of this method saves transient information about the state of the activity's view hierarchy, such as the text in anEditText
widget or the scroll position of aListView
widget.
Caution: You must always call the superclass implementation of
onSaveInstanceState()
so the default implementation can save the state of the view hierarchy.
To save additional state information for your activity, you must override
onSaveInstanceState()
and add key-value pairs to theBundle
object that is saved in the event that your activity is destroyed unexpectedly. For example:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the
android:id
attribute.
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the
onStop()
method.
Restore your activity state When your activity is recreated after it was previously destroyed, you can recover your saved state from the Bundle that the system passes to your activity. Both the
onCreate()
andonRestoreInstanceState()
callback methods receive the same Bundle that contains the instance state information.
Because the
onCreate()
method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the stateBundle
is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.
For example, the following code snippet shows how you can restore some state data in
onCreate()
:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
Instead of restoring the state during
onCreate()
you may choose to implementonRestoreInstanceState()
, which the system calls after theonStart()
method. The system callsonRestoreInstanceState()
only if there is a saved state to restore, so you do not need to check whether the Bundle is null:
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Caution: Always call the superclass implementation of
onRestoreInstanceState()
so the default implementation can restore the state of the view hierarchy.