How to correctly handle application deactivation and reactivation
Much has been written about tombstoning and how important it is that your application be able to save and restore state once it goes into the background. This is all good.
One of the finer points of the Windows Phone application model that has received less attention is the case where your application moves into the background and does not get tombstoned. In the current implementation of the Windows Phone operating system, there are two cases where your application will be deactivated but not tombstoned:
- When calling certain launchers and choosers, the system attempts to keep your process alive in order to give a seamless Back experience for the user
- If the user accidentally leaves your app (eg, via the Start or Search buttons) and immediately hits Back to return to your application
Most developers are familiar with the first case because it is a core part of application development. Many developers, however, are not familiar with the second case because it's something you don't typically encounter during normal development and testing. Let's look at the different sequence of events that happen in the tombstone case and the non-tombstone case:
Tombstone Case (typical)
- Current page gets OnNavigatedFrom
- Application gets Deactivated
- Process dies
- Process starts
- Application gets constructed
- Application gets Activated
- Current page gets constructed
- Current page gets OnNavigatedTo
Non-Tombstone Case (Start -> Back)
- Current page gets OnNavigatedFrom
- Application gets Deactivated
- Application gets Activated
- Current page gets OnNavigatedTo
See what's missing? That's right, none of your constructors get called in the non-tombstone case, because they were never removed from memory. To see this in action, try the following:
Create a default Windows Phone application in VS
Add the following line inside the App class (right after the declaration of RootFrame):
public static GeoCoordinateWatcher GCW = new GeoCoordinateWatcher();
Add the following lines inside the Deactivate handler (because you want to be a good citizen and clean-up after yourself)
if (GCW != null)
GCW.Stop();
GCW = null;
And a Button to MainPage and put the following line inside the Click handler:
App.GCW.Start();
Now run the application and click the button. Nothing should happen (which is fine).
Now hit the Start key immediately followed by the Back key, and try clicking the button again. If you are lucky, nothing will happen (same as before) but if you are unlucky the application will crash and you will be returned to the Start menu. If you are having trouble reproducing the problem, add the following line to your Deactivated handler to simulate the application doing some work during deactivation, such as saving data to IsoStore:
Thread.Sleep(2000);
By looking at the sequence of events above for the non-tombstone case, it should be obvious why the application is crashing - GCW is implicitly created in the application's static constructor (by virtue of it being a field initializer), but it is destroyed in the Deactivated event. Since the application is then re-activated without being re-constructed, GCW remains null and the call to Start() will fail.
In order to fix the problem, we simply recreate GCW in the Activated event, and now all is fine:
if (GCW == null)
GCW = new GeoCoordinateWatcher();
Another approach would be to remove the field initializer altogether, and to unconditionally construct the object in both the Launching and Activated events (possibly via a shared initialization routine). Of course performance best-practices would say to do none of this but to lazily create the object only when it is first needed, but that makes the example harder ;-). The point is that you don't have to undo everything in the activated event - you can be lazy - but you must ensure that your application can cope with being reactivated without being tombstoned. A similar sequence of events can occur for your pages, where they receive OnNavigatedFrom followed by the OnNavigatedTo without being destroyed and re-constructed in the meantime.
Note that this behaviour is covered in the existing Windows Phone documentation, but not everybody reads the docs (which is unfortunate). Please read the documentation :-)
Comments
Anonymous
December 14, 2010
the docs say: "For this reason, it is important not to do anything destructive in the Deactivated event handler. Your application should be able to continue after completing the Deactivated event handler without the Activated handler being called." which gives the impression that you should not do any cleanup in Deactivated and more importantly, that Activated will not be called again in this pseudo-tombstone scenario. your description of the lifecycle clearly shows that the Activated event will be called again - it would be good to clarify that in the docs, and to even add the simple lifecycle examples to make it very clear to folks.Anonymous
December 14, 2010
Brian, you're right. I'll let the doc team know. Thanks!Anonymous
December 17, 2010
Very useful indeed! I didn't even realise this worked this way.Anonymous
January 13, 2011
Very good to know