A good mobile platform should acknowledge the hardware constraints that mobility imposes on the device. Compared to desktops, mobile devices have less memory, less processing power, limited screen real-estate and limited battery life. Add up these constraints and you should conclude that, on a non-dedicated device where many applications will run, applications will eventually be closed or shut down to make resources available to other applications.
Windows Phone deals with this constraint through a feature called tombstoning. While it may seem like tombstoning would be a straightforward proposition, it’s actually a point of contention among developers. Some argue it shouldn’t be needed. Others argue that it’s too difficult. The rest of us simply hate the name of the feature. Still, mobile device constraints make it a necessary evil, so great mobile applications must be able to handle tombstoning.
Most new Windows Phone developers come to the platform with the expectation that an application’s lifecycle looks something like:
Windows Phone 7 challenges this expectation by exposing a different lifecycle—one that’s less process-oriented and more session-oriented.
In Windows Phone 7 you should think of the lifecycle as:
The benefit of this new session-oriented model is that users can navigate across apps without having to think about how the OS is managing its resources. A user doesn’t care if interrupting their game to reply to an incoming SMS message will kill the process for the game. The user should expect that he can get back to the game when finished with the message. If that works well enough, the underlying details are irrelevant.
The downside for developers is that there’s a bit more to handle to truly provide the perception of session continuity because your sessions are still running on a traditional, process-centric OS. To accommodate sessions in a process-centric world, you create logical states for the session: Launched, Activated, Running, Deactivated, Tombstoned and Closed (or Ended).
Figure 1 shows the practical lifecycle for a Windows Phone 7 app. The application lifecycle events, described in Figure 2, are exposed by the Microsoft.Phone.Shell.PhoneApplicationService class.
Figure 1 Windows Phone 7 Application Lifecycle
Figure 2 Application Lifecycle Events
User is exiting the application by pressing the back key on the main page.
When exiting, the user expects to come back to a new, fresh application.
The tombstoned state is a bit more complicated and not directly related to a PhoneApplicationService event. When an application is deactivated, the OS does not immediately kill the process for that application. In theory, the OS kills the application when it needs resources and when it happens. The application doesn’t get a notification at all, and it’s simply killed.
In practice, Windows Phone 7 kills the process quickly after control is transferred to another foreground application, but this is not a detail you should count on. Microsoft already announced at Mobile World Congress last February that improvements such as Fast App Switching are coming, so don’t rely on an implementation detail to determine when tombstoning happens. Instead, prepare for it by doing the right work at deactivation.
A phone has many processes running all the time (shell, phone and so on), but it has, at most, one application running in the foreground. (It can have zero when nothing is running in the foreground.)
When a foreground application transfers control to another application or to an OS component, it gets deactivated. After a process is deactivated, the OS might kill the process to release the resources. This is called tombstoning.
As you can see, tombstoning does not happen every time the app is deactivated, but tombstoning always comes after a deactivation. In fact, Deactivated is the last event PhoneApplicationService fires before tombstoning, so this is where you must do your work to activate the app again later.
Figure 3 shows all the different tasks that lead to deactivation, and guesses the likelihood of tombstoning occuring.
Figure 3 Deactivation Tasks
There’s a subset of choosers that don’t tombstone immediately, but can still be tombstoned if the user takes an action that tombstones the process. These include PhotoChooserTask (unless the user specifies crop), CameraCaptureTask, MediaPlayerLauncher, EmailAddressChooserTask and PhoneNumberChooserTask.
All other choosers and launchers tombstone right after the Show method is called.
To see the Windows Phone 7 application lifecycle in action, launch the LWP.TombStoning sample in the code download.
Because the goal for session-based navigation is to make it easy for the user to jump across foreground applications seamlessly, you must save all relevant state in the Deactivated event and restore state in the Activated event. Most applications have three types of state to manage:
Persistent application state should be saved in IsolatedStorage or via the ApplicationSettings class. Application state should be saved as early as possible, in case the battery runs out, for example.
If it’s user data (such as a session cache) and you don’t want to serialize too often, then it should be saved both on the Deactivated and Closing events, and (symmetrically) it should be restored in the Activated or Launching events.
Session-specific state can be saved in isolated storage if you want control over serialization formats or you have too much data, or it can be saved in the PhoneApplicationService.State dictionary. You should save it only in the Deactivated event and restore it in the Activated event.
Page-specific state should be saved in the PhoneApplicationPage.State dictionary. The key to saving page state is to remember that the application has a back stack of pages that will be serialized automatically when PhoneApplicationService.Deactivated occurs. To keep your pages ready for tombstoning, you must listen for the PhoneApplicationPage.OnNavigatedFrom override in your page and save any view state that’s not yet committed to your Model (or ViewModel) in the page’s dictionary. Don’t wait until the Deactivated event, because by then you can’t get to the pages in the back stack.
Of course, if you save page state when you recieve OnNavigatedFrom, you should restore it in the OnNavigatedTo override for the page.
You could also save page-specific state in ViewModels, and then serialize your ViewModels as session state, but that would require saving uncommitted state on a ViewModel, so I don’t recommend it. Leverage the infrastructure in place to stay future-proof for later optimizations in the platform.
Tombstoning is not difficult. It’s just a bit of tedious work and it demands consistency and planning. If your app is deactivated but not tombstoned, your state will remain in memory and will not be reconstructed.
Avoid relying on class constructors that create state that your application needs, but that might be released during deactivation. Symmetry is preferred. Use the PhoneApplicationService Deactivated and Activated events for app-level state, and OnNavigatedFrom or OnNavigatedTo for page state.
If you have objects (singletons) in your app that are instantiated outside of activation calls (maybe due to delay instantiation), always check whether they’re properly constructed and initialized before trying to use them. A common mistake that I’ve encountered is reading data in the PhoneApplicationService.Activated event or PhoneApplicationPage.OnNavigatedTo and not resetting it. Pages can be NavigatedTo multiple times (regardless of tombstoning) and even a session can be tombstoned multiple times during a session.
After you’ve restored state, clear it. You can set it later in theNavigatedFrom override for your page or the Deactivated event for the app.
Be smart about what you save and when you restore it. Part of making it seamless for the user to come back to the app is restoring your application promptly. If you save too much of either page or application state, it will slow down activation. Leverage isolated storage if needed to background load state that might not be required immediately when your application is activated. Both activation and deactivation should happen in less than 10 seconds. Be aware of this or the OS may kill your process before it finishes deactivating or as it’s reactivated. You should, however, aim for much less than 10 seconds.
Understand the constraints of the serialization framework. You can, at most, store around 2MB across all of your page and application state. If your total adds up to more, you’ll start seeing exceptions when you navigate and when you deactivate. You shouldn’t be serializing this much data in page state. If you need to cache large data sets, keep them in isolated storage.
Use query strings for page navigation. If you must pass context into a new page, use the query string passed to the page to either pass all the data or pass a unique identifier (a token) to a service locator that can fetch the data based on that token. Don’t assume ViewModels or page state are available when your pages are activated after tombstoning.
Understand your choosers and launchers. Not all of them tombstone, and there are specific rules on how to wire up event listeners for choosers. For these rules, please read “How to: Use Choosers for Windows Phone” at msdn.microsoft.com/library/windows/apps/ff769543(v=vs.105).aspx.
Be mindful of the relationship between OnNavigatedTo and PhoneApplicationPage.Loaded. Within OnNavigatedTo, the visual tree for the page is not fully built yet. Often you’ll have to extract restored state, but wait until the page’s Loaded event to restore the UI state. Examples of actions that must be delayed include setting Focus, setting SelectedIndex in a pivot and scrolling.
If you’re doing a lot to save and restore data—and you shouldn’t be—consider some advanced optimizations. Note that you should do this only if necessary and make sure to test thoroughly.
To detect tombstones, set a flag on your app class in Deactivated. If the flag isn’t reset on Activated, it means you weren’t tombstoned and all your pages should still be in memory and require no restoration. To combine it with detection for tombstoning in pages, you can use a token for each activation within a session.
Another optimization is to listen for the page’s OnNavigatingFrom override and detect direction. If NavigationMode is going back, the page will be destroyed and there’s no reason to save state for it.
Again, planning is the key to proper tombstoning. Don’t leave tombstoning for the end of the app development cycle and try to retrofit it. Plan it early, implement it as you go and test thoroughly.
One last tip for developers is to think hard about making the experience seamless. Windows Phone 7 controls allow you to get around a page easily. To make it truly seamless for the user—to feel like he never left a page or app—you should consider restoring the following:
The one you shouldn’t restore is SelectedItem in a panorama. The panorama doesn’t support this, and setting the DefaultItem in a panorama is not the same as panning to the correct page. I recommend that you avoid using DefaultItem as a means to get back to the panorama item selected before tombstoning.
To see these tips in action, launch the LPW.TombstoningWithState sample in the code download. The readme.txt file has pointers and scripts for each scenario.
This article is by no means a comprehensive reference to tombstoning. But now that you know what to look for, start introducing some tombstoning patterns into your own Windows Phone 7 apps. I think you’ll see right away how much it improves the user experience.
Jaime Rodriguez is a principal evangelist at Microsoft driving adoption of emerging client technologies such as Silverlight and Windows Phone 7. You can reach him on Twitter at twitter.com/jaimerodriguez or read his blog at blogs.msdn.com/jaimer.
Thanks to the following technical expert for reviewing this article: Peter Torr
More MSDN Magazine Blog entries >
Browse All MSDN Magazines
Subscribe to MSDN Flash newsletter
Receive the MSDN Flash e-mail newsletter every other week, with news and information personalized to your interests and areas of focus.