Sunday, October 24, 2010

WP7 Deactivated != Tombstone

James Ashley reported WP7 Deactivated != Tombstone in this 10/21/2010 post:

With the transition from the Beta WP7 Dev tools to the RTM, an important and subtle change was introduced to the way launchers and choosers work.  In the beta, it was a given that every time we launched a task from Silverlight, the current application would be tombstoned and the Deactivated event would be thrown on the current PhoneApplicationService object.

image26With the RTM tools, this is no longer always the case.  Five tasks break this rule: CameraCaptureTask, EmailAddressChooserTask, MediaPlayerLauncher, PhoneNumberChooserTask and PhotoChooserTask.  In each case, while the application may be tombstoned, it also may not be.  In fact, most of the time, it will simply be paused and no tombstoning will occur – the application will not be terminated.  

We can assume that the typical workflow for a chooser task is the following (the images below demonstrate the PhoneChooserTask in action):

phonechooserback

A user performs an action that initiates a task.  The task window opens. The user either completes the task (in this case by selecting a contact) or presses the Back button to cancel the action.  The user is returned to the previous page.  In this case, it makes sense that no termination occurs.  Instead, the app is left in a Paused state much as an app is paused during incoming and outgoing calls – timers are suspended, no events are handled.

[Note: in the RTM, no event is fired when an application goes into a paused state.  At best, you can handle RootFrame.Obscured and RootFrame.Unobscured for incoming and outgoing calls.]

However, the user may also decide to press the Start button at the third step.   At that point it makes sense for termination to occur as it is less likely that the user will backpress back to the app.phonenumberchooser

So when should we handle the deactivated event for the second case where the user moves on and doesn’t complete a chooser task?  We actually can’t handle it when tombstoning occurs because our app is paused and will not respond to any events.

Instead, the PhoneNavigationService.Deactivated event is fired when chooser task (or MediaPlayerTask) is initiated.  This despite the fact that we don’t know (and can’t know) at this point whether the app will actually be tombstoned.

So far so good.  We may unnecessarily be writing objects to storage if the app isn’t ever tombstoned, but it’s better to be safe than sorry.

What is peculiar is that when we return to the app – either through the first scenario above or through the second deviant scenario – PhoneNavigationService.Activated is always thrown.  There’s a symmetry to this.  If the Deactivated event is called and we back into the application, then the Activated event will be called. 

The somewhat annoying thing is that the PhoneApplicationService should have enough information to avoid firing false Activated events unnecessarily.

No matter.  There is a simple trick for finding out whether an Activated event is real or fake – whether it truly follows a tombstoning of the application or is simply thrown because Deactivated was previously called.

Use a flag to find out if the App class was newed up.  It only gets newed up in two situations – when the application is first launched and when it is recovering from tombstoning.  Set the flag to false after the App class has finished loading.  If a chooser is launched and the application is paused but not tombstoned, the flag will still be false.  If tombstoning occurs, the flag will be reset to true.

private bool _isNewApp = true;

private void Application_Launching(object sender
, LaunchingEventArgs e)
{
_isNewApp = false;
}

private void Application_Activated(object sender
, ActivatedEventArgs e)
{
if (_isNewApp == true)
{
// a real tombstone event occurred
// restore state
}
_isNewApp = false;
}


If you are handling tombstoning at the page level, you can similarly use a flag to determine whether the page was re-newed or not.

bool _isNewPage = true;

public MainPage()
{
InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (_isNewPage)
{
//restore state
}
_isNewPage = false;
}

The important thing to remember, though, is that the Deactivated / Activated events are not synonymous with tombstoning.  They simply occur alongside of tombstoning – and in the special cases described above occur when no tombstoning happens at all.


James is the author of Patterns of Windows Phone Architecture Part III and earlier.


No comments:

Post a Comment