One difference to between conventional programming and Android development is that the Android OS can at any time kill your Activity if it runs low on resources or in certain other conditions like configuration changes.
This can be very critical when it comes to data. On such an event, there is a possibility that data loss will occur and as programmer you want to avoid that under any circumstances.
Most of the standard widgets like EditText already preserve their state under certain circumstances. By default widgets/UI elements with an android:id attribute defined, will by default preserve their states when the application is destroyed and recreated.
But what if the you need to preserve data defined inside of your Activity or your own widgets?
In the first part of this tutorial, I’ll show you how to preserve data within the Activity when the activity gets destroyed. There are a few events when this can happen.
- Configuration change
- Screen orientation change (i.e. changing from portrait to landscape mode)
- Localization change (i.e. switching from English to German language)
- Low resources
- Running out of RAM
In most cases, the developer has no control over such events and they are hardly predictable. This makes it important to always temporary save critical application data when the Activity lose it’s focus.
Every time the Activity change it’s states in which it could possibly killed, the onSaveInstanceState(Bundle outState) method is called. In this method the developers can temporary save all critical data which will preserved when the Activity is killed for any of the reasons mentioned above. If the application was killed in the meantime, it will be recreated and onRestoreInstanceState(Bundle savedInstanceState) will be called. The savedInstanceState will also be passed to the onCreate(Bundle savedInstaceState) method. At this point we can get the data and resume the operation.
onRestoreInstanceState(Bundle savedInstanceState) will only be called if the Activity was destroyed while will be called every time when there is a possibility that the Activity can be killed by the system.onCreate(Bundle savedInstaceState) will only contain a valid savedInstanceState reference, when the Activity is being recreated. So the first time, your application is started, will be null! Remember that, when you’re reading the data inside the onCreate(Bundle savedInstaceState) method!onCreate(Bundle savedInstaceState) method, it’s advised to move the more CPU intensive initializations inside the onRestoreInstanceState(Bundle savedInstanceState) methodA typical application
To demonstrate this, I’ve created a very simple example. It will consists of only 2 TextViews, one AutoCompleteTextView and one Button.
As usual you create a new Android Application in Eclipse or the IDE of your choice.
First, the XML layout file used for the application, main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/howto"
>
</TextView>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/product"
>
</TextView>
<AutoCompleteTextView
android:id="@+id/productlist"
android:completionThreshold="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</AutoCompleteTextView>
<Button
android:id="@+id/btnShowSelected"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Show selected"
>
</Button>
<TextView
android:id="@+id/result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</TextView>
</LinearLayout>
It’s quite simple and nothing new in here. So I’ll skip it to the actual code of the Demo application.
SavedStateExample1.java:
package com.tseng.examples.SavedStateExample1;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class SavedStateExample1 extends Activity {
TextView lbResult;
Button btnShowSelected;
AutoCompleteTextView acProductList;
final private static String products[] = new String[] {
"Apple",
"Ananas",
"Apricot",
"Banana"
};
private String selectedProduct;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set Layout
setContentView(R.layout.main);
// Get references to the widgets we need
acProductList = (AutoCompleteTextView)findViewById(R.id.productlist);
lbResult = (TextView)findViewById(R.id.result);
btnShowSelected = (Button)findViewById(R.id.btnShowSelected);
btnShowSelected.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
lbResult.setText("Selected: " + selectedProduct);
}
});
// Create adapter
ArrayAdapter<String> priceListAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
android.R.id.text1,
products);
acProductList.setAdapter(priceListAdapter);
acProductList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
selectedProduct = (String)adapter.getItemAtPosition(position);
}
}
);
}
}
Before we continue, a few explanations for developers new to Java/Android development.
final private static String products[] = new String[] {
"Apple",
"Ananas",
"Apricot",
"Banana"
};
private String selectedProduct;
The products[] variable holds oure data for the AutoCompleteTextView which we will bind to the AutoCompleteTextView widget with the code below. selectedProduct will simply hold the String from the selected list item.
// Create adapter
ArrayAdapter<string> priceListAdapter = new ArrayAdapter<string>(this,
android.R.layout.simple_dropdown_item_1line,
android.R.id.text1,
products);
acProductList.setAdapter(priceListAdapter);
acProductList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
selectedProduct = (String)adapter.getItemAtPosition(position);
}
}
);
There we create an simple ArrayAdapter which will manage the underlying data. Also we add an OnItemClickListener which will trigger, when the user has selected an item from the auto-complete list. Implementing Listeners is already explain in my previous post “Implementing Listeners in Android/Java” and “How to implement your own Listeners in Android/Java”
Now we can launch our demo application and test it out.
First enter the first letter of the product you want to test, in the screenshots above the letter “A” and select one of the items, i.e. Ananas. Once selected it will appear on in the EditText View. If we click on “Show selected”, the text “Selected: Ananas” will appear, indicating us that the value was correctly saved.
So far everything is working. But what happens if you change the orientation of the screen (i.e. by pulling out the keyboard of the T-Mobile G1? Let’s test it out.
First thing we notice is that our result TextView (displaying “Selected: Ananas”) is gone. The reason is because by default it’s empty and changing the orientation by default forces the Activity to be destroyed and recreated with the new orientation.
If you want to preserve data permanently, you need to save it either as Preferences or in a ContentProvider/database. But that’s another topic!
I could get it simply back, by pressing “Show selected” again or set it programmatically, you may think now. Try it out, you’ll be surprised. Instead of showing “Selected: Ananas”, it shows only “Selected: null”.
What happened?
When the screen orientation was changed, the Activity was destroyed and recreated. So the selectedProduct variable wasn’t initiated after the recreation.
In worst case, this could lead to data loss or your application/Activity could throw an Exception and crash. As developer you need to prevent such cases. In order to prevent this to happen, we’ll need to save our important data in an temporary savedInstanceState object and get it when it was recreated.
So first, we’re creating a static variable, which will used as key to save and retrieve the variable.
final private static String PRODUCT_TITLE = "title";
final static string so if you later need to change it, it’s enough to change it at one single place.
There won’t be any negative performance effects if you use the final and static keywords together. The compiler compile it inline, so it’s same as writing “title” on every place you need to use the key.
Next we need to Override two Activity-methods: onSaveInstanceState and onRestoreInstanceState and add our code to handle the saving of the savedInstanceState.
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
selectedProduct = savedInstanceState.getString(PRODUCT_TITLE);
if(!TextUtils.isEmpty(selectedProduct)) {
// Update lbResult
lbResult.setText("Selected: " + selectedProduct);
}
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
/**************************************************
* Here we do _temporary_ save all critical data,
* like our selectedProduct.
**************************************************/
outState.putString(PRODUCT_TITLE, selectedProduct);
super.onSaveInstanceState(outState);
}
As we can see, it’s very easy to save temporary data which will be preserved when the Activity is destroyed. Additionally in the onRestoreInstanceState-method we’re updating the lbResult TextView field.
If we now start our demo application and repeat the steps from above, we’ll see that this time the “Selected: Ananas” remains visible after screen orientation change and it’s not “Selected: null” anymore, like in the screenshot below:
- Session data
- Cursor positions
- Calculated values
You should NOT save values, which may be invalid or outdated when the Activity is recreated (i.e. the Activity was in background for a long time). For example:
- Current location (this can get outdated very quickly)
- Current time
- Socket/Http Connection objects (instead only save the cookie or session data and recreate a new Socket/Http Connection when the Activity is restored)
As always, the full code of SavedStateExample1.java:
package com.tseng.examples.SavedStateExample1;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class SavedStateExample1 extends Activity {
TextView lbResult;
Button btnShowSelected;
AutoCompleteTextView acProductList;
final private static String PRODUCT_TITLE = "title";
final private static String products[] = new String[] {
"Apple",
"Ananas",
"Apricot",
"Banana"
};
private String selectedProduct;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set Layout
setContentView(R.layout.main);
// Get references to the widgets we need
acProductList = (AutoCompleteTextView)findViewById(R.id.productlist);
lbResult = (TextView)findViewById(R.id.result);
btnShowSelected = (Button)findViewById(R.id.btnShowSelected);
btnShowSelected.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
lbResult.setText("Selected: " + selectedProduct);
}
});
// Create adapter
ArrayAdapter<String> priceListAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
android.R.id.text1,
products);
acProductList.setAdapter(priceListAdapter);
acProductList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
selectedProduct = (String)adapter.getItemAtPosition(position);
}
}
);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
selectedProduct = savedInstanceState.getString(PRODUCT_TITLE);
if(!TextUtils.isEmpty(selectedProduct)) {
// Update lbResult
lbResult.setText("Selected: " + selectedProduct);
}
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
/**************************************************
* Here we do _temporary_ save all critical data,
* like our selectedProduct.
**************************************************/
outState.putString(PRODUCT_TITLE, selectedProduct);
super.onSaveInstanceState(outState);
}
}
Next time, I’ll tell you how to preserve SavedStates for widgets, as it’s quite differently than that one of an Activity. The Activity accepts Bundle-object which will hold the saved values. In your own widgets however, you’ll have to implement your own SavedState class.
No related posts.






Of course, what a great site and informative posts, I will add backlink – bookmark this site? Regards, Reader.
THX
Worked perfektly
thank you
Nice explanation , very helpful.
Very Nice Explanation,It is very useful to me Thanks…