23 July 2012

Using a provisional WinRT port of SilverlightSerializer to store state in MVVMLight ViewModels

Over 1.5 years ago I showed how to store your Windows Phone application state (‘tombstoning’) using SilverlightSerializer by Mike Talbot. In my quest to leverage hard-won Windows Phones skills to be usable in Windows 8 I made a provisional port of SilverlightSerializer 1 to WinRT. That is, in C#.

Since my win8nl library port is a bit behind, I’ve stuck the class in a simple project that you can download here.

Usage is as follows: in App.xaml.cs, you define two methods: SaveState and RestoreState:

private async void SaveState()
{
  var file = 
    await ApplicationData.Current.LocalFolder.CreateFileAsync(
      "AppState.dat", CreationCollisionOption.ReplaceExisting);

  using (var fileStream = await file.OpenStreamForWriteAsync())
  {
    SilverlightSerializer.Serialize(MainViewModel.Instance, fileStream);
  }
}

Pretty simple: when the state needs to be saved, create a file, get a stream, and let SilverlightSerializer do its magic. As for RestoreState:

private async Task RestoreState()
{
  try
  {
    var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();
    var dataFile = files.FirstOrDefault(p => p.Name == "AppState.dat");
    if (dataFile != null)
    {
      using (var fileStream = await dataFile.OpenSequentialReadAsync())
      {
        MainViewModel.Instance = 
          SilverlightSerializer.Deserialize(fileStream.AsStreamForRead()) 
            as MainViewModel;
      }
    }
  }
  finally
  {
    if (MainViewModel.Instance == null) MainViewModel.CreateNew();
    MainViewModel.Instance.Start();
  }
}

This basically tries to deserialize a viewmodel from a file “AppState.dat”. If that fails, it creates a new MainViewModel, and starts it. The whole idea behind this is described in the original Windows Phone 7 post, so I encourage you to read that if you have no idea what I am doing here.

Now the important bit is when to call this methods. If you think the most logical place for RestoreState is in OnLaunched, you are right. The first few lines of my OnLaunced method in App.xaml.cs look like this in my app:

if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
  Window.Current.Activate();
  return;
}
else
{
  await RestoreState();
}

Now the most logical place to call SaveState is of course OnSuspending, and we are done, right?

Wrong.

To my own utter surprise, yesterday, after a long debugging session, I found a weird edge scenario. I asserted the following: if a user closes an app by the “Swipe Down gesture”, it takes about 10 seconds (on my Samsung Slate 7) before OnSuspending is called. When the user restarts the app within those 10 seconds from the start screen, the state is not stored yet, or too late – in any case it is not yet available to the (yet again) starting app. So you don’t get the last state, but the state before that, as I shamefully discovered when I proudly showed my state save method to an Application Excellence Lab Microsoftie :(

Don’t get me wrong – the actual time SaveState runs in my app is about 0.047 seconds. It’s lightning fast – just like the original SilverlightSerializer. But the time between user closing the app and the actual state save being fired up was killing my state.

The solution to this is very simple: when the user closes the app, Window.Current.VisibilityChanged is fired. Not like after a few seconds, but instantly. So add and event listener to that at the bottom of OnLaunched, that calls SaveState , like this:

 Window.Current.VisibilityChanged += (o, e) => {if (!e.Visible) SaveState();};

Basically: if the visibility of the current windows changes to invisible, write the state. Maybe you write app state too often (for example because the user temporarily puts it in the background, then VisibilityChanged is fired as well) but at least you circumvent the problem I ran into. Since state writing takes very little time, better often than not at all, I’d say.

And in OnSuspending? Well, you could call SaveState from there, too. That’s useful when your app does something in the background and it’s state changes even when the users has put it in the background. Then, when the OS kicks it into suspension because it runs out of memory, the state is saved too.

Caveat emptor: I kinda roughly hacked Mike Talbots work to operate in the WinRT world. The current state is ‘works for me’. I commented out the whole section about Dictionary support, so I suppose that does not work. My ultimate goal is to convert SilverlightSerializer 2, but that was too big a bone to chew in a short time. It serves my ported game Catch’em Birds fine. I hope to get it finished soon.

2 comments:

peSHIr said...

A quick link that might have pointed you to the need for (also) using VisibilityChanged: http://dotnetbyexample.blogspot.nl/2012/06/detecting-your-windows-8-app-is-running.html ;-)

Joost van Schaik said...

@peshir Yes THANK you dear colleague that occurred to me as well ;-)