Learning Xamarin–Preserving State

By September 25, 2013Xamarin
[Please see the Table of Contents for this series.]

In the previous post we looked at the Application Lifecycle.  In this post we’ll put some of that information to practical use, and create an application that preserves a little state across being Stopped. 

This is an adaptation of the walkthrough available on the Xamarin site; however I dive a bit deeper into where to set yoiur resources and have simplified the example to focus on the key aspects of preserving and restoring state.

 

Getting Started

Begin by creating a new Xamarin application in Visual Studio.  Name it SavingState.   Notice that Visual Studio creates an Activity for you which it names Activity1 and that in that activity it has created a count member variable and initialized it to 1.  It has also created a Button for you (named MyButton) and it has created an event handler that counts the clicks of the button.  Our work is almost done (!)

Fire up your emulator and run the program.  Notice that as you click the button the number of clicks is incremented.  The problem is that the number of clicks is being managed locally in the method and not being preseerved at all.  We can fix that, and we can use the log (described in detail here) to monitor the life-cycle changes of the application as we load and unload the state.

Let’s begin by changing the private member variable name from count to _counter to match my naming conventions.  There is no reason to do this except naming consistency. While we’re at it, change the initialization from 1 to 0, after all the button has not been clicked at all yet.

We’re going to set the counter variable in OnCreate by retrieving it from the bundle.  Add the following code under the SetContentView line,

if (bundle != null)
{
Log.Debug(GetType().FullName, "Restoring counter value");
_counter = bundle.GetInt("click_count", 0);
}

The Log.Debug method takes the name of the process (SavingState) and a string to display.

The second argument to bundle.GetInt (0) is the default value to use if the Bundle doesn’t have a key “click_count”, which it won’t the first time you run the program.  We then retrieve the button using the existing code but wee add a line to set the button Text property out of our resources,

button.Text = Resources.GetString(
Resource.String.counterbutton_text, _counter);

For this to work, take a moment and open the files Resources->Values->Strings.xml and add this line:

<string name="counterbutton_text">
I’ve been touched %1$d times.</string>

(Note that I’ve broken this into two lines to fit in the blog, but you want to add it as a single line.)  Save and close the resource file.

Let’s modify the button.Click event handler to more clearly reveal its behavior, and to use our resource string,

button.Click += (object sender, System.EventArgs e) =>
{
_counter++;
button.Text =
Resources.GetString(
Resource.String.counterbutton_text, _counter);
};

Preserving the State

In order for the _counter value to be preserved in the bundle we need to override the OnSaveInstaceState event handler.  We use PutInt to store the _counter variable in the bundle, which is a dictionary, using a string as the key: “click_count”.  We then call the base implementation to ensure that all values are saved properly,

protected override void OnSaveInstanceState(Bundle outState)
{
Log.Debug(GetType().FullName, "Saving instance state…");
outState.PutInt("click_count", _counter);
base.OnSaveInstanceState(outState);
}

Watching the LifeCycle

There are a number of other event handlers we can override to watch and log the various life-cycle events,

protected override void OnDestroy()
{
Log.Debug(GetType().FullName, "On Destroy");
base.OnDestroy();
}

protected override void OnPause()
{
Log.Debug(GetType().FullName, "On Pause");
base.OnPause();
}

protected override void OnRestart()
{
Log.Debug(GetType().FullName, "On Restart");
base.OnRestart();
}

protected override void OnResume()
{
Log.Debug(GetType().FullName, "On Resume");
base.OnResume();
}

protected override void OnStart()
{
Log.Debug(GetType().FullName, "On Start");
base.OnStart();
}

protected override void OnStop()
{
Log.Debug(GetType().FullName, "On Stop");
base.OnStop();
}

When you start debugging, be sure to either invoke View->Other Windows->Android Device Logging, or click the Android Device Logging button in the Xamarin.Android toolbar as shown in the figure.

AndroidDeviceLogging

Your log will reflect the various life-cycle steps.  You can zero in on just the entries for this application by clicking on the drop down list next to FilterBy and choosing SavingSate.SaveState as shown in the figure below,

AndroidLoggingSavingState

Seeing The Restore

You’ll notice as you use the application that the information is not restored. As you know from reading about the life-cycle, there are few circumstances under which your application will be killed – typically only when paused when memory is low.  So how can we prove that the storage and restoration is working?  Remember that when you rotate the device the instance is destroyed and recreated.  Aha!  That is the test for us.  Start up the application, and then click the rotate button on the emulator and check the log,

DestroyandRestoreLogged

The highlighting reflects the destruction of the Activity and its restoration and then finally the restoration of the data.  If you comment out the override of OnSaveInstanceState and run the program again, when you rotate the screen the number of clicks is lost.  This demonstrates conclusively that storing away the InstanceState and restoring it works and is essential.

Note however that OnSaveInstanceState is not guaranteed to be called; it is possible for your application to be killed before OnSaveInstancer can run.  Thus, OnSaveInstance should only be used to persist UI state. Persistent app data changed in your Activity needs to be stored in a database or file system or preferences

 

About the Author

JesseJesse Liberty is a Master Consultant for Falafel Software, an author and he creates courses for Pluralsight.    Liberty  hosts the popular Yet Another Podcast and his blog is required reading. He was a Senior Evangelist for Microsoft,  a XAML Evangelist for Telerik, a Microsoft MVP, Distinguished Software Engineer at AT&T; Software Architect for PBS and Vice President of Information Technology at Citibank. Jesse can be followed on twitter at @JesseLiberty

The following two tabs change content below.