(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
Last time, I described the various events and methods that come into play when the user leaves your Windows Store application. In this second part, I’m going to describe what happens when the user returns. Not all of the notifications are guaranteed to occur—it depends on various factors. If the user merely switched focus to a different app without removing yours from the screen, you’ll see considerably less activity when the user returns than if your application had left the screen. And much also depends on whether Windows got as far as suspending and then terminating your application while it was off the screen.
In that last case, where your application was suspended and then shut down, you will see the following sequence when the user switches back:
ApplicationExecutionState.Terminated
OnNavigatedTo
LoadState
VisibilityChanged
(becoming visible)Activated
The second and third of these only happen as a result of how the Visual Studio templates for Window Store apps work. The LoadState
method isn’t part of WinRT—it’s a member of the LayoutAwarePage
base class that the template defines. And although OnNavigatedTo
is part of WinRT, it doesn’t run by default on a restart. It’s only called as a side effect of some work done by the templates.
If Windows suspended your application but kept it in memory, you will see the following events when the user switches back:
Resuming
VisibilityChanged
(becoming visible)Activated
If Windows did not get around to suspending your application before the user switched back to it, you’ll just see the following.
VisibilityChanged
(becoming visible)Activated
And if your application remained on screen while the user switched to a different application (e.g., because your app is sharing the screen with another, snapped app) you’ll just get the Activated
event.
Activated
is therefore the only event you get in all cases. And you get VisibilityChanged
in all cases where your application had been off the screen.
I’ll now describe the various events and methods in more detail.
If Windows suspended and then terminated your application, it will need to re-launch it if the user tries to switch back. Remember, when Windows terminates an app to make more memory available for the foreground application, it hides this from the user. The application still appears in the Alt+Tab list, and in the list that appears when you swipe down from the top left (which you can also show by typing WindowsKey+Tab). Windows wants to maintain the illusion that terminated applications continue to run in the background, so it has no choice but to start a new instance when the user tries to return to an app that isn’t really still there. Windows lets you know that this is happening through the PreviousExecutionState
property of the LaunchActivatedEventArgs passed to your OnLaunched method. This will have a value of ApplicationExecutionState.T
erminated
if your application is being launched because the user wants to return to it.
When your application is re-launched in this way, you should put things back exactly how they were when the application was suspended. This may entail loading application data, and will typically also involve restoring superficial state such as which page the user is on, and where lists are scrolled to.
The Visual Studio templates provide code that restores some of the superficial state for you. It generates code in OnLaunched
that calls the RestoreAsync
method of the SuspensionManager
class (also provided as part of the template). This in turn calls the SetNavigationState
method on each Frame
your application registers. (The templates register just a single frame by default—it acts as the root of your whole UI. But it’s possible to have multiple frames each with its own navigation stack.)
Recall from last time that the templates arrange to call GetNavigationState
during the Suspending
event. That method returns a string representing the state of the navigation stack, and the template saves this to disk. When the templates pass that string back to SetNavigationState
, it restores the stack. So this will ensure that whichever page the user was on last time, they’ll still be on that page now even though the application restarted. Moreover, this preserves the back/forward stack.
As we also saw last time, GetNavigationState
has the side effect of calling the current page’s OnNavigatedFrom
method. Likewise, SetNavigationState
will call OnNavigatedTo
on the object it instantiates to represent the current page.
WinRT invokes a page object’s OnNavigatedTo when the user navigates into it, whether using Back or Forward buttons, or just tapping or clicking on something that navigates to a new page. Left to its own devices, this is the only situation in which it’ll call the method. But if you’re using the Visual Studio templates, it will also be called during a restart following silent termination of an application, for the reasons described in the preceding section.
There isn’t any easy way for the OnNavigatedTo
method to know whether it is being called due to intra-app navigation, or an application restart—the event argument reports a NavigationMode
of Back
for restarts, the same as for a normal intra-app backwards navigation.
In general, if the navigation mode indicates some sort of return (either Back
or Forward
, not New
or Refresh
) your application would be expected to restore the superficial user interface state to whatever it was the last time the user was on this page. You need to do this for either intra-app navigation or a relaunch, so the distinction between those two situations doesn’t matter; if you care about the distinction you should use a different notification.
Be aware that you will only see this notification either for intra-app navigation, or in cases where the application was terminated after suspension. This method does not get invoked when the application resumes after suspension.
If you’re using the Visual Studio templates, in practice you’ll typically rely on its support for persisting and retrieving data in the back, forward, suspend, and relaunch scenarios, and will instead override the LoadState
method.
The LayoutAwarePage
base class supplied by the Visual Studio templates defines a virtual method called LoadState
. It calls this from its OnNavigatedTo
implementation, passing back data that you provided in the most recent call to your page’s SaveState
method, which I described in the previous blog post. (It will only call Load
State
for Back
or Forward
navigation by the way.)
The advantage of using SaveState
and LoadState
over the underlying OnNavigatedFrom
and OnNavigatedTo
methods is that the templates handle the work of saving data out to disk during suspension, and loading it back in during a restart.
For intra-app navigation, you will get SaveState
and LoadState
notifications immediately and consistently as you navigate away and back, but for suspension, resumption, and restarts, it is slightly less consistent. First, there’s a delay—SaveState
will be only called if the user switches away from your app for long enough to trigger suspension. Even when that does happen, you won’t necessarily get a corresponding call to LoadState
. That happens on a relaunch, so it requires Windows to terminate your process after suspending it (and it turns out to do this fairly infrequently—it’s very common for it just to keep suspended applications around in memory for a long time). Although I can now see the logic of this, it surprised me at first—I had initially assumed that I’d get a LoadState
whenever the user returned to the app.
The three notifications discussed so far only happen when the app is re-launched after Windows has silently terminated it. The next is for a different scenario.
If the user switches away from your app for long enough that Windows decides to suspend the app (meaning you’ll have seen the Suspending event described in the previous post) and if the user later returns to your application without Windows having terminated the process in the meantime, your process will be allowed to continue running, and the Resuming event will be raised to let you know what happened.
Your process will have been in suspended animation, so from the perspective of your code, it’ll look like the Resuming
event was raised more or less immediately after the Suspending
event. Time will have elapsed in between these of course, and your code would be aware of that if it looked at the system time for both events. But since your application will have been suspended, it won’t get to run any code in that gap, so as far as your application’s activity is concerned, these two events are adjacent.
You might not need to do anything in the Resuming
event. One of the implications of receiving this event is that all the work you did to preserve your application’s state during suspension was unnecessary: your process remained in memory, so the state is all still there. That said, there is still sometimes important work to do on resumption. If your application shows information that needs to be kept up to date, you should check how long you were in suspension for, because you might need to refresh your display. (E.g., if your application shows upcoming train departure times, you might need to remove some or all of the trains from the screen, because some of them may already have left while you were suspended; if you were suspended for hours, they may well all have left.) But note that when your application does need to do something during Resuming
, it will not be the mirror image of what happened during Suspending
. (If you do work that mirrors the state saving done during suspension, it’ll normally be done during launch, possibly indirectly via the LoadState
method.)
Just to be clear, if your application was suspended and then terminated, you will not see a Resuming
event. Your application will be restarted, and OnLaunch
will receive a PreviousExecutionState
of ApplicationExecutionState.T
erminated
, as described earlier.
When a user returns to your application after it has been off screen, you will always get a VisibilityChanged event from the core window. That’s true whether your application was suspended, and then resumed, or was suspended, terminated, then restarted, or even if the user flipped away and back again so quickly that Windows didn’t even have time to suspend your application.
Remember, you get the same event when becoming visible or invisible, with the difference being indicated through the event argument’s Visible
property (which is why I qualify this event with either “becoming visible” or “becoming invisible” each time I refer to it in these two blog posts).
If you stopped any activity (e.g. video playback) when this event indicated that your application had become invisible, this is likely to be the right time to restart that work.
No matter how the user returns to your application, the last notification you will see is the core window’s Activated event. If your application stayed on screen while the user moved their focus elsewhere (perhaps because they have multiple applications on screen simultaneously) then this will be the only notification you receive when the user returns.
If you need to change your application’s appearance in any way to reflect where the input focus is, you would need to handle this. And as Mike Taulty pointed out to me after my previous post, there may be situations in which deactivation is actually a better time to stop certain kinds of work than the moment of becoming invisible. For some games it might make sense to pause automatically as soon as the user moves the focus away, even if your application is still visible. If you’ve chosen to do that, then it might be appropriate to resume when reactivated.
I’ve put quite a lot of detail in the last two posts, so the most important point might be buried in the volume. So it’s worth highlighting:
It’s not all about suspension and restarts.
Proper suspension handling for Windows Store apps gets a disproportionate amount of attention in the documentation, and also in forums, blogs, and other community efforts. This is presumably because it’s a feature of apps on constrained devices that traditional Windows client developers won’t be used to. But it’s not the right place to hang certain kinds of logic, because suspension is delayed, and doesn’t always occur, and even when your app is suspended, there are two completely different paths by which the user can return to it. This means by extension that LoadState
and SaveState
are also not good places to put work that must be done when the user switches into or out of your application. The less widely discussed VisibilityChanged
and Activated
events of the CoreWindow
class usually better for this kind of work.