In my original post here “Is my XNA game dead yet?”, I covered most of the basics of tombstoning, that being the process of what happens to your app or game during it’s life on the Windows Phone.
The Windows Phone 7 Team on their blog also added to the mix with “Understanding the Windows Phone Application Execution Model, Tombstoning, Launcher and more” with Part 1, Part 2 and more importantly Part 3.
Now tings have moved on and the understanding of how to handle things correctly has changed, so I've updated the guidance here with a few personal notes.
I also now know that tombstoning is also an extreme sport, where you hurtle your body off some large object in to the sea (see the above picture care of the Times Newspaper). Don’t try this yourself as this guide will not help you with that at all!!
The main focus of the last article was about handling tombstone events. If a call comes in or you launch a chooser / launcher, then you app is terminated. From the point at which the Deactivated event is fired you have approx. 20 seconds in the background to save the state of you application / game before the phone kills the process forcibly. If the phone kills you app/game them any information you couldn’t store in time will be lost, there is even a chance that you will loose any data you tried to store.
So the key guidance is to keep the information you need to store during a tombstone (deactivated event) to a minimum. The process should be something like the following for games:
- Game starts from cold
- Menu displayed (loading content while it’s displayed)
- Launch game
- Update values as play progresses
- When a deactivate event arrives, store a flag to tell the game it resumes where to pick up from
- Store any additional live information required to return the player back to the previous state / position (keep it light)
- When focus is returned to game, check the Start-up mode in the game constructor and kick off a background thread to load intensive data
- In the activated event, check there was a previous state and if one exists load the game back to it previous place
- Start playing (optionally, also put up a paused screen to give the user time to return to the game)
For Silverlight apps, this is similar:
- App starts from cold
- User performs activity in the app.
- If navigated to a different page store the page name (and any values needed to launch that page)
- If navigating to a different index on a page, store that index as it changes
- If data is returned from the web, cache it and then display it (if requested again, look in cache first)
- When a deactivate event is fired, store a flag in the phone state to note deactivation
- When the app is re-launched, check the start-up mode and validate any caches you have while the app resumes
- In the activated event, return the user to the page and index they were viewing and re-bind any data to that page.
In short, use the isolated storage and settings cache during the app to keep any Phone application state objects to a minimum (DO NOT STORE IMAGES IN THE PHONE APPLICATION STATE!) and tailor the start-up process to be as efficient as possible. Don’t assume that if the start-up mode is “Activating” means that the Phone state cache has any values, always test.
A place for everything and everything in it’s place
So to recap, we have a few places to store things while our app is running:
This is the main storage for WP7 application, you can store what ever you like in and have almost unlimited storage (well almost). There are no quota limits for isolated storage but be aware of how much you use as most phones only have 8Gb available and most users would like to use that for other applications as well!.
With isolated storage you can have multiple files and folders and can even iterate through the files and folders stored there pretty much the same way you do on the PC.
Isolated Storage Settings cache
The settings cache is a simple serialised storage dictionary area. It allows you to store single objects against a single string lookup. Pretty much the same way a List or dictionary works in code.
Again there is no limit to this settings cache, only limitation is that it only supports simple XML serialisation, so is no good for things like images or complex classes.
Phone Application State storage
The application state area is almost identical to the Isolated storage settings cache with a few little differences. First and foremost, it only lets you store up to 2 Mb of data (for performance reasons as the phone is starting or stopping).
The other point is about access, the phone application state store is ONLY available during the tombstoning events “activated” and “deactivated”, if you try to access it outside of this you will get an exception message (most of the time!!, see later). It is not available in the application constructor (before the activated event is fired) and is not available after the deactivated event is complete (not in the onExited or onClosed events for example)
It may be available during the running of your application but this is not guaranteed.
The purpose of this store (unlike the Isolated type stores) is to provide ADDITIONAL information to your application when it is resuming only, to allow you to return the application back to it’s previous state before it was interrupted by another event, such as a phone call or calling out to a Launcher or Chooser task.
Also if the application is exited cleanly (or the phone is powered off), then the Phone state store is cleared (most of the time), so it is not persistent storage like the others. I’m keep to get this point across because I’ve seen far too many tutorials or comments stating this is the place to store your data regardless of what it is used for. Like everything else know what to use and when and why.
One main thing to keep in mind with all of these storage options is that they are application specific, only your app can see or access your apps files / folders and no sharing is allowed. The only way for two apps to share data is through the cloud or your own custom web service/site on the internet.
The code for using Isolated storage is fairly well described and detailed already, so I won’t go over that again. I’d recommend also using Nick Gravelyn’s “EasyStorage” project on codeplex. It’s fairly well documented on there and a good resource to use.
Using the isolated Storage settings dictionary is also well documented on MSDN here and operates much the same way as the Phone application state dictionary as described in the previous article. So saving a value becomes as easy as:
IsolatedStorageSettings.ApplicationSettings.Add("My Setting", "My Value");
And retrieving it using:
String setting; if (IsolatedStorageSettings.ApplicationSettings.Contains("My Setting")) setting = IsolatedStorageSettings.ApplicationSettings["My Setting"] as String;
**Always test, else it will crash out with an exception if the key does not exist!!
However once point to make is to make use of this store during the running of the application or game, it’s not just for saving data for the user/player. So if you need a cache for your application (highly recommended for web based apps or using images).
So if you are navigating between pages in Silverlight or navigating between pages on a pivot or panorama, then store the setting as it changes in the Isolated Settings store.
If you get a results set back from the web into a list, then store it as it is returned and dispose it when the results are no longer needed.
in games, you can use it to track the players score/ lives or level or even completed levels or history, you don’t need to wait until the player saves or when a Tombstone event comes in, do it progressively as the game runs.
Can you tell me a bit about where you are from
An often overlooked feature of the Phone application model is the phone Start-up Mode. Within the PhoneApplicationService class, there is a state object that tells you how the phone started the application. As the MSDN help suggests:
So in your applications constructor (or LoadContent function) you can check how the application was started and perform any warm up functions you need to BEFORE the activated event is called. This is important when you are watching your start-up times, you don’t need to push every test or check in the activated event when resuming. (also read the next session and take care when the phone THINKS it’s reactivating)
To make use of this state just test the state in your applications constructor, like so:
if(PhoneApplicationService.Current.StartupMode == StartupMode.Activate) //Do something special for activation
Now in one of the more bizarre turn of events when dealing with tombstoning, there are cases where tombstoning is called but never actually happens. Now this might seem odd but is one to look out for else you will be caught out, the main causes of this are:
- Message boxes in XNA
- Using the SIP keyboard in XNA
- SOME of the other services provided by the guide services (not including the marketplace launcher)
- When the screen locks (now confirmed it does not close the app / game always)
- If the user unfolds a hardware keyboard (confirmed on some devices but not all)
The basic rule of thumb is test all the ways that your app/game is going to be used. Use the debug window to track when events happen and use in memory values to test if the app is actually tombstoned when the activated event is returned.
Also remember the emulator is NOT an actual device or an exact replica of one. These behaviours also differ on an actual phone.
On several occasions in the apps/games I’ve developed I've fallen foul of this in different places, so be careful. Just because the deactivated / activated events fire does not always mean that your app has been disposed, check for it, else at it’s best your game will just restart or worse, it will cause memory issues trying to get content into memory twice and cause the app/game to crash.
The quick brown fox jumps over the lazy dog
Now some players are just lazy or not paying attention, for these situations you need to manage the idle features of the device, else it will mange them for you. Remember sometimes, it’s not the players fault, they might be waiting for something to happen in your game.
The idle detection works in two ways:
- User Idle detection
Defined by a period of inactivity by the user, such as no touches to the screen. This is configured on the phone itself (although not till a later update, for now it’s fixed?), you can disable it though code in your application using (you can also enable it by setting it to enabled):
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
- Application Idle detection
Defined by a period of inactivity by the application, such as no page activity or updates. This is again configured by the user but can be disabled / enabled in code using:
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
Idle detection is something you should really think about if you are using any power dependant sensors or features (like the Accelerometer or GPS) or if long periods of inactivity means the player has put their phone down to do something else. The MSDN help is a but vague on the correct use of idle detection and boils down to state that you should really just do it your self and not use the phone idle state management.
This is a bit of a mixed bag really, but something to really consider while designing your game.
The other comment in the MSDN help is also part of the certification requirements, you MUST prompt the user before disabling (or enabling?) the phone idle detection, else your app / game will fail submission.
My view is that only consider disabling the Phones inbuilt idle detection logic if you have a very good reason to, like if there are situations where you expect long periods of inactivity from the user (like watching a video!) or a cut scene (I don’t believe I said that). Else implement your own logic or ensure something happens at regular intervals (and protect the battery at all costs!)
This section of the MSDN provided my best ever smirk. consider the following except from the MSDN help on idle detection “the operating system may deactivate an application for other reasons, such as a depleted battery.”. Really?, the operating system will stop your app, if the battery runs out, eat that Einstein.
Enough of this hilarity.
Well that’s enough from me again for a while, things to do, people to see and games / apps to write.
Once I find some more useful bits I'll be back. At some point I will continue the 2D tutorial series and even (maybe) start up the 3D tutorial.
I do have some other ideas in minds but for now (motivation depending), I actually want to get some work done, lol.
You can reach me on twitter @DDReaper or contact me though this blog if you have any glaring questions, C&C welcome as always and if you have a good point, I'll update this article.
Always test on a real device, there are some things different between the emulator and device, not including the performance difference between the two (even some things that REALLY should not be different). As an example, one of the applications I wrote was very dependant on some backend WCF services and for some reason on the emulator I had no issues talking to those services (unless I was behind a proxy, but that is a another story) but when it was submitted to the market place it failed (badly), upon further testing on some real devices, it would not get data from the web service, even though it reported the connections were successful.
It looks like I’ve fixed that now but I'm still going to test some more before resubmitting as I still do not know why it didn’t work, even though I now have it working (I didn’t change anything fundamental, so WHY?) Test Test Test and then test some more!