Fast App Resume in your app should be implemented in a manner that ensures a predictable experience for the user. The behavior of your app when it is resumed should depend on the entry point of your app when it was initially launched from a cold start and the entry point when it was resumed.
There are two kinds of entry points. Primary entry points occur via a primary tile on the Start Experience or an app link in the App List or Games hub. Secondary entry points occur via a deep link from a secondary tile, a toast notification, or from an extensibility point such as a protocol handler.
The Windows Phone OS maintains a navigation history for your app. When the user taps the hardware Back button, the OS navigates back through the stack of previously visited pages. With Fast App Resume the OS attempts to navigate to a URI within your app. If the URI is different from the current page of the app, a new page instance is created and placed on the navigation back stack. If the URI is identical to the app’s current location, no new page is created and the app’s current page is displayed.
If the entry point is your app’s primary tile, for example, the URI is your app’s main landing page. When an app is resumed via a primary entry point, it can respond in one of the following two ways:
It can start fresh by clearing the back stack and navigating to the app’s main landing page. If the user navigates backward from the resumed app, because there are no pages in the history the app will exit, returning the user to the Start Experience or the previous app.
It can resume as if nothing happened by canceling navigation to the requested URI. In this case, the user arrives on the page that he last viewed and the back stack from the previous session remains intact.
Although it is possible to resume as if nothing happened whenever the user launches your app from a primary entry point, after a few minutes of inactivity it is better to start fresh. The user may no longer be mindful of the app’s presence in the background and will expect that relaunching the app starts a new instance. In the sample for this section, you see how to detect and expire a Fast App Resume in this scenario.
Implementing Fast App Resume gets more complicated when the user launches or resumes an app from a secondary entry point. For example, if the user launches the app via its primary tile and then taps the hardware Start button, the app is deactivated and waits to be resumed. If the user then navigates to the Photo hub and relaunches the app from the picture viewer, the app should resume by clearing the back stack and navigating to the requested deep link URI.
Table 3.1 shows the recommended behavior for resuming via a primary or secondary launch point after being launched from either a primary or secondary entry point.
An appropriate place for detecting and coordinating Fast App Resume is in your project’s App
class. The App
class in the WPUnleashed.Examples project, in the downloadable sample code, responds to the various Fast App Resume scenarios.
When the sample app is deactivated, the App
class records the time of deactivation in the app’s isolated storage settings. When the app is activated, a field is set to the result of the HasFastResumeExpired
method, which is shown in Listing 3.1.
Although it is not absolutely necessary for the purposes of Fast App Resume to store the deactivation time in isolated storage, it does allow the value to be used for other purposes in which tombstoning may occur.
bool HasFastResumeExpired()
{
DateTimeOffset lastDeactivated;
if (settings.Contains(DeactivationTimeKey))
{
lastDeactivated = (DateTimeOffset)settings[DeactivationTimeKey];
}
TimeSpan duration = DateTimeOffset.Now.Subtract(lastDeactivated);
return TimeSpan.FromSeconds(duration.TotalSeconds)
> fastResumeExpiryTime;
}
You saw earlier that an app can be launched or resumed via a primary or secondary entry point. The App
class uses a custom enum named SessionType
to record if the app is navigating to the app’s home page via a primary entry point or to a deep link URI via a secondary entry point. SessionType
can be either None, Home, or DeepLink.
Each time a navigation event is raised, the App
class’s RootFrame
_Navigating
handler determines the SessionType
using the NavigatingCancelEventArgs
object’s Uri
property (see Listing 3.2). If the session type has not been recorded and the navigation mode is set to New, it indicates that the app is launching. When the navigation mode is equal to Reset, a Fast App Resume is underway.
During a Fast App Resume, if the current page URI is not the same as the requested URI, the root frame’s Navigating
event is raised twice; once for the page that was displayed before deactivation and once for the requested URI. If the current page URI is the same as the requested URI, the Navigating
event is raised only once for the page that was displayed before deactivation.
void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
string url = e.Uri.ToString();
if (sessionType == SessionType.None && e.NavigationMode == NavigationMode.New)
{
/* App is launching. */
if (url.Contains("?"))
{
sessionType = SessionType.DeepLink;
}
else if (url.Contains("/MainPage.xaml"))
{
sessionType = SessionType.Home;
}
}
if (e.NavigationMode == NavigationMode.Reset)
{
/* The current navigation is the result of a Fast App Resume.
* If so the URI is different from the current page URI
* then another navigation will follow. */
lastNavigationWasReset = true;
}
else if (e.NavigationMode == NavigationMode.New && lastNavigationWasReset)
{
/* This block will run once if the previous navigation
* was the result of a Fast App Resume. */
lastNavigationWasReset = false;
if (url.Contains("?"))
{
/* We reach this point if a secondary entry point was used,
* such as via the secondary tile
* created in FastAppResumeViewModel.cs */
sessionType = SessionType.DeepLink;
/* The page navigation stack will be cleared. */
}
else if (url.Contains("/MainPage.xaml"))
{
if (sessionType == SessionType.DeepLink)
{
/* When the app was previously launched via a deep link URI
* and relaunched via its primary tile,
* the back stack needs to be cleared. */
sessionType = SessionType.Home;
}
else
{
if (!fastResumeExpired)
{
/* The app was previously launched via its primary tile
* and relaunched via its primary tile.
* Cancel the navigation to resume. */
e.Cancel = true;
shouldClearJournal = false;
}
}
}
fastResumeExpired = false;
}
}
The root frame’s Navigated
event is raised after its Navigating
event, unless the Cancel
property of the NavigatingCancelEventArgs
is set to true.
The RootFrame_Navigated
handler, shown in Listing 3.3, keeps track of whether the previous navigation was a result of a Fast App Resume, which is indicated when the NavigationMode
property of the NavigationEventArgs
is equal to Reset. If so, and the timeout period has not expired, the page navigation stack is cleared using the RemoveBackEntry
method of the root frame.
void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
NavigationMode mode = e.NavigationMode;
if (previousNavigationWasReset && mode == NavigationMode.New)
{
if (shouldClearJournal)
{
Debug.WriteLine("Clearing history.");
/* Clear the entire page stack. */
var rootFrame = (PhoneApplicationFrame)RootVisual;
while (rootFrame.RemoveBackEntry() != null)
{
/* Nothing to do. */
}
}
else
{
shouldClearJournal = true;
}
}
else
{
previousNavigationWasReset = mode == NavigationMode.Reset;
}
}
The FastAppResumeView.xaml page located in the FastAppResume directory, in the WPUnleashed.Examples project, allows you to try out the different Fast App Resume scenarios. A button on the view is bound to an ICommand
named CreateShellTileCommand
that places a secondary tile on the Start Experience (see Listing 3.4). Resuming the app from the secondary tile causes the page navigation stack to be cleared.
public class FastAppResumeViewModel : ViewModelBase
{
string tileUrl = "/FastAppResume/DeepLinkedView.xaml?DeepLink=true";
public FastAppResumeViewModel()
{
createShellTileCommand = new DelegateCommand(
obj =>
{
if (!TileExists())
{
CreateTile();
}
},
obj => !TileExists());
}
bool TileExists()
{
ShellTile shellTile = ShellTile.ActiveTiles.FirstOrDefault(
x => x.NavigationUri.ToString().Contains(tileUrl));
return shellTile != null;
}
void CreateTile()
{
StandardTileData tileData = new StandardTileData
{
Title = "Unleashed Secondary",
};
/* Pin the tile to Start Experience.
* This causes a navigation event and deactivation of the app. */
ShellTile.Create(new Uri(tileUrl, UriKind.Relative), tileData);
}
readonly DelegateCommand createShellTileCommand;
public ICommand CreateShellTileCommand
{
get
{
return createShellTileCommand;
}
}
}
Fast App Resume is an important new feature of the Windows Phone 8 OS that allows apps to forgo the usual startup penalty when being relaunched. Apps implementing this feature appear to start faster and give the impression of having better performance than apps that do not.
18.220.191.247