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:

  1. 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
  2. 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)

  1. Current page gets OnNavigatedFrom
  2. Application gets Deactivated
  3. Process dies
  4. Process starts
  5. Application gets constructed
  6. Application gets Activated
  7. Current page gets constructed
  8. Current page gets OnNavigatedTo

Non-Tombstone Case (Start -> Back)

  1. Current page gets OnNavigatedFrom
  2. Application gets Deactivated
  3. Application gets Activated
  4. 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:

  1. Create a default Windows Phone application in VS

  2. Add the following line inside the App class (right after the declaration of RootFrame):

    public static GeoCoordinateWatcher GCW = new GeoCoordinateWatcher();

  3. 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 :-)