This is Day # 6 in the Windows 8 development article series on common tips & tricks towards real-world Windows 8 Store apps. Now that our Windows 8 App is starting to behave like a good citizen, time to take care of data – the crux of most applications & also make sure that our app lives its lifecycle cleanly. Over the next several weeks, you’ll see 8 articles talk about some must-do things for Windows 8 app developers. Simple & to the point, with some code examples on XAML/C# stack. Here’s the indexed list for the series:
Day 1: Know the ecosystem; Start
Day 2: Layout, Navigation & Visual States
Day 3: Semantic Zoom
Day 4: Controls & Styling
Day 5: Search, Share & Settings Contracts
Day 6: Data Persistence & Application Life-Cycle Management
Day 7: Use of OData or Web Services
Day 8: Live Services integration
Day 6: Data Persistence & Application Life-Cycle Management
It is not by chance that the topics of persisting data in Windows 8 Store Apps and the Application Lifecycle Management have been chosen together; they are very much inter-related and the timing of actions matter. Data – whether application or user specific, is gold & needs to persisted correctly, across uses of the same application. Application lifecycle, on the other hand, deals with all the states that a Windows 8 App goes through in between launch & termination. It is this lifecycle that exposes checkpoints where we App developers get chances to persist our data correctly. Let’s explore our options in the C#/XAML stack ..
Data Persistence
Before we start talking about saving data, let’s first see what type of data we may be dealing with. For most Windows 8 Store apps, data can largely be classified under two categories:
- App Data – settings, files, cache, session state, custom lists etc.
- User Data – documents, credentials, photos, music etc.
Windows 8 apps, especially when used on tablets, should provide for an intimate UX. This is why managing the data persistence becomes important, to increase user confidence & usage of apps. Windows 8 Store apps run in their own sandbox, for the obvious reasons of application security. Data leaks, possible malware or other catastrophes are much less of a headache to the end user with this setup. So, applications get their own sort of Isolated Storage to read/write data to disk, much like what we have been used to from Windows Phone development. However, that is not the only option – remember, Windows 8 is a cloud-connected OS. With the benefits of the one consolidated MSFT Account, wouldn’t it be cool if 3rd part Apps could also sync data between the same App running on multiple PCs? Yep, possible!
So, here are the options for data persistence for Windows 8 Store Apps:
- Data can be persisted, as Application Settings in key-value pairs, with the value being set as per application needs. Simple settings, strings, and all the way up to serialized objects can be persisted. This type of data is usually stored in the registry, but actual mechanism of storage is transparent to developers. Within the App data store, each app gets its root container to store information, with the flexibility of supporting custom nested containers.
- Data can also be saved as Application Files, which is usually stored in the sandboxed file system access that Apps have. While a pre-defined root directory is present, any nested folder structures are supported for better organization of data.
- The application data API exposes three possible storage targets:
- Local – for data persistence on the particular device only.
- Roaming – for making the data available for sync across multiple devices running the same App under MSFT account. Windows roams app data opportunistically and doesn't guarantee an instant sync.
- Temporary – for transient storage that can act as a cache; but may be removed up by user/system cleanup.
- The lifetime of application data storage is tied to lifetime of the App on the given device; upon un-installation, all of the App’s sandboxed data is cleaned up, locally & in the cloud.
Let’s look at some examples through code:
1: // Reference to Local Application Settings.
2: Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
3:
4: // Reference to Roaming Application Settings.
5: Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
6:
7: // Persisting simple Application Settings.
8: localSettings.Values["NumberOfArticlesToDownload"] = 20;
9: roamingSettings.Values["LastReadArticleID"] = 5;
10:
11: // Reading settings back.
12: int numberOfArticlesToDownload = Convert.ToInt16(localSettings.Values["NumberOfArticlesToDownload"]);
13: int lastReadArticleID = Convert.ToInt16(roamingSettings.Values["LastReadArticleID"]);
14:
15:
16:
We’ll see examples of persisting Application Files in the next section.
Application Lifecycle Management
Every Windows 8 Store app goes through several lifecycle stages between installation & removal, and also on every application run. Having an App that is a well-behaved Windows 8 citizen in this lifecycle management is important, since it truly ensures a consistent UX for the end user.
The Windows 8 App Execution States are captured by the following illustration:
Let’s break down the different execution states:
- On fresh App launch, Windows 8 applications will display a Splash screen and try to start showing the first page of the App.
- During the time the Splash screen shows, the custom UI, data bindings & registering of event handlers is happening at a frantic pace. Since we will only get a few seconds to show the first page, heavy operations should be avoided to quicken application load time. The App.xaml.cs file exposes an OnLaunched Activation event handler for being notified when the app starts up. Once done, the splash screen is torn down & the App enters the Running state.
- In addition to the normal launch by tapping on App Tile, Windows 8 Apps may be launched through several Contract implementations (like Search, Share Target, File opener etc.). Each one would add an Activation checkpoint in App.xaml.cs to take appropriate action in the event handler & restore state if needed.
- Once an App is running, the user may step away from it, thus putting it in the background. When the user moves an app to the background, Windows waits a few seconds to see whether the user might immediately switch back to the app. If the user does not switch back, Windows suspends the app. This triggers the App to go into a Suspended state immediately. Developers get a OnSuspending notification in App.xaml.cs. As we shall see next, this really is the only opportunity to save relevant application state & user data for persistence. If an app does not save state & return from the suspending event within 5 seconds, Windows assumes that the app has stopped responding and terminates it.
- While in Suspended state, the App, with all its contents, is just being held in memory by Windows, although it does not get CPU cycles (except for background tasks). This is to support fast application switching, so that, when the user comes back to your app through the application backstack, it is immediately re-hydrated & running again. This is called App Resume, and most application developers should not have to do anything with Windows bringing it back from memory. However, if network content or something else needs to be refreshed after substantially long suspension, a Resuming event handler provides notification.
- Now lot of apps in suspension might eventually stretch system resources, at which point Windows decides to Terminate suspended Apps. This is when apps are truly killed with all resources being freed up. Developers will not get a notification when their apps are terminated; so the Suspending event handler remains the only indicator to persist appropriate content. The apps enter a Not Running state upon termination.
- Although users generally do not need to close Windows 8 apps, they can choose to do so through a gesture swipe or hitting Alt+F4. Or a running application may have crashed due to unforeseen circumstances, bringing the user back to the Start screen. In both these cases, the app is terminated and enters the Not Running state. If one needs to differentiate between termination mechanisms, the ApplicationExecutionState enumeration may be used to inspect last execution state.
- The user may navigate the backstack & eventually come back to an App that has been terminated by Windows. When the app determines that it is activated after being terminated, developers should take care to load the application data that it saved during suspend so that the app appears to the user just as it did when it was suspended.
Let’s look at some code to understand the persistence of data at different execution states of the Windows 8 App. We go back to our demo SilverlightShow Windows 8 application that primarily shows us a list of categorized articles:
Now, at the heart of this applications data is a list of SilverlightShow articles. We have defined our own class and essentially have a globally accessible “ObservableCollection<SLShowArticle>” that is used for data-binding. In real world, we would pull down latest articles and maintain/cache this collection of articles for use throughout the application. So, it makes sense that on suspension, we try to persist this entire collection and on resume from termination, we’ll try to rehydrate our collection back. We’re going to use simple DataContractSerializer serialization/deserialization to persist our collection in a local file. Let’s look at some code:
1: private async void OnSuspending(object sender, SuspendingEventArgs e)
2: {
3: var deferral = e.SuspendingOperation.GetDeferral();
4:
5: // Save application data.
6: await SuspensionManager.SaveAsync();
7: deferral.Complete();
8: }
9:
10: private const string articleListFileName = "serializedArticlelist.xml";
11: public static async Task SaveAsync()
12: {
13: // Save the navigation state for all registered frames
14: foreach (var weakFrameReference in _registeredFrames)
15: {
16: Frame frame;
17: if (weakFrameReference.TryGetTarget(out frame))
18: {
19: SaveFrameNavigationState(frame);
20: }
21: }
22:
23: // Push current Article list to persistent Storage as File.
24: MemoryStream articleListData = new MemoryStream();
25: DataContractSerializer articleListSerializer = new DataContractSerializer(typeof(ObservableCollection<SLShowArticle>));
26: articleListSerializer.WriteObject(articleListData, ((App)Application.Current).ArticleCollection);
27:
28: StorageFile articleListfile = await ApplicationData.Current.LocalFolder.CreateFileAsync(articleListFileName, CreationCollisionOption.ReplaceExisting);
29: using (Stream fileStream = await articleListfile.OpenStreamForWriteAsync())
30: {
31: articleListData.Seek(0, SeekOrigin.Begin);
32: await articleListData.CopyToAsync(fileStream);
33: await fileStream.FlushAsync();
34: }
35: }
As you see above, we are using the event handler to get notified that our App is suspending and trying to save our collection of articles in a named XML file in local isolated storage. The Application Data API gives us access to the LocalFolder, where we get to write out our file using a serialized file stream. Now, we did all this just for demonstration; in reality, there is a SuspensionManager class that is added by most templates in the Common folder, that does pretty much exactly what we did. So, simply add your data collection type (may need Data Contract decorations for serialization) to the SuspensionManager’s KnownTypes, and the rest should be transparent. Now, how about re-hydrating this data back on resume? Here’s the code:
1: protected override async void OnLaunched(LaunchActivatedEventArgs args)
2: {
3: // Do not repeat app initialization when already running, just ensure that the window is active.
4: if (args.PreviousExecutionState == ApplicationExecutionState.Running)
5: {
6: Window.Current.Activate();
7: return;
8: }
9:
10: // Create a Frame to act as the navigation context and associate it with a SuspensionManager key.
11: var rootFrame = new Frame();
12: SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
13:
14: if (args.PreviousExecutionState == ApplicationExecutionState.Terminated || args.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
15: {
16: // Restore the saved session state only when appropriate.
17: await SuspensionManager.RestoreAsync();
18: }
19: else
20: {
21: // Initialize sample data.
22: this.ArticleCollection = LoadSampleData();
23: }
24:
25:
26: if (rootFrame.Content == null)
27: {
28: if (!rootFrame.Navigate(typeof(ArticleList)))
29: {
30: throw new Exception("Failed to create initial page");
31: }
32: }
33:
34: // Place the frame in the current Window and ensure that it is active.
35: Window.Current.Content = rootFrame;
36: Window.Current.Activate();
37: }
38:
39: public static async Task RestoreAsync()
40: {
41: // Restore any registered frames to their saved state
42: foreach (var weakFrameReference in _registeredFrames)
43: {
44: Frame frame;
45: if (weakFrameReference.TryGetTarget(out frame))
46: {
47: frame.ClearValue(FrameSessionStateProperty);
48: RestoreFrameNavigationState(frame);
49: }
50: }
51:
52: // Restore Global Article List from Isolated Storage.
53: StorageFile articleListfile = await ApplicationData.Current.LocalFolder.GetFileAsync(articleListFileName);
54: using (IInputStream inStream = await articleListfile.OpenSequentialReadAsync())
55: {
56: // Deserialize & re-hydrate TaskList.
57: DataContractSerializer serializer = new DataContractSerializer(typeof(ObservableCollection<SLShowArticle>));
58: ((App)Application.Current).ArticleCollection = (ObservableCollection<SLShowArticle>)serializer.ReadObject(inStream.AsStreamForRead());
59: }
60: }
Not too difficult, is it? We check to see how the App was terminated, and if needed, fetch the file from storage and rehydrate our object model back, for the rest of the app to utilize. And that’s it, we have accomplished the basics of data persistence across our application’s lifecycle.
Conclusion
That’s it for today. The crux of this article was to talk about the various data persistence mechanisms available to Windows 8 Store apps and how to leverage different checkpoints in the Application’s Lifecycle to save our content.
See you next time as we dive into Service Integration from Windows 8 Store Apps. Thanks for reading!
About Author
Samidip Basu (@samidip) is a technologist, gadget-lover and MSFT Mobility Solutions Lead for Sogeti USA working out of Columbus OH. With a strong developer background in Microsoft technology stack, he now spends much of his time evangelizing Windows Phone/Windows 8 platforms & cloud-supported mobile solutions in general. He passionately helps run The Windows Developer User Group (http://thewindowsdeveloperusergroup.com/), labors in M3 Conf (http://m3conf.com/) organization and can be found with at-least a couple of hobbyist projects at any time. His spare times call for travel and culinary adventures with the wife. Find out more at http://samidipbasu.com.