(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) |
A quick quiz for you: in a C# XAML-based Windows Store application, what’s the distinction between a page’s OnNavigatedFrom
and SaveState
methods, the application object’s Suspending
event, and the core window’s Activated
and VisibilityChanged
events? All of these are typically invoked or raised round about the time a user switches away from your app, but why are there so many?
More importantly, which one should you use? In my experience, the answer is: not always the one the docs or web searches might imply.
There’s a similar and related plethora for when the user returns to an application. You might see any of following being invoked or raised: the page’s OnNavigatedTo
and LoadState
methods, the application object’s OnLaunched
method and Resuming
event, and the core window’s Activated
and VisibilityChanged
events (those last two being the same events that the core window raises when switching away, but with different arguments this time).
Sometimes you’ll get all of these events, but there are several variations on the themes of switching away from and returning to the app, and in some cases you only get a subset of the notifications.
OK, so there probably aren’t strictly 50 variations, and only about half of mechanisms I’ve just described are concerned with returning to the app, but I don’t think there is a song called 11 Ways to Leave or Return to Your Lover. My point is that there are lots of different bits of the API all doing quite similar things.
You might be wondering why anyone would care. Well, I happen to have a deep interest in how things works, but there are also good practical reasons to understand this stuff. For example, you might need to do something around the time that these events and methods come into play so that when the user switches away from your application, and then later returns to it, your application doesn’t present any unpleasant surprises.
In the resource-constrained world of tablet computers, unpleasant surprises are, unfortunately, the default. (Or at least, they would be if it weren’t for the fact that to get your app listed in the store, you’re required to do the work to handle most of the nasty cases correctly.) There’s quite a high chance that after the user switches away from your application, Windows will decide to terminate your process. This enables it to let whatever application the user is actually using to have the memory your process was occupying. The basic principle here is that the machine’s resources should be dedicated to whatever the user is doing now, and if that means summarily terminating processes the user’s not currently interacting with, then so be it.
Windows doesn’t tell the user, though. It acts as though your program is still there—it’ll be available from the list of tasks the user can swipe in from the side (or WindowsKey+Tab if you’re more of a keyboard user). If the user switches back to a program that Windows has already destroyed, it just launches a new copy, passing in launch parameters that tell the application that it was previously terminated and is now expected to carry on as if nothing had happened.
If you don’t take steps to handle this, you will likely upset the user. Your program must detect on startup that the user was unaware that you’d ever been shut down, and you must attempt to put everything back how it was before Windows pulled the rug out from under your feet. If you don’t write code to handle all this, the user will wonder why your application has inexplicably decided to return to its start page. (And you might not even get as far as having users—your app might be rejected from the store certification process.)
Consequently, it’s important for your application to be aware of when the user switches away and back, so that you can preserve the illusion of availability. And that’s why you might care about some or all of the many methods and events I described at the start of this blog post.
Here are some things you might want to do in your application around the time the user switches to a different application:
MediaElement
)There are some related processes when the user switches back:
There’s already a catch. You might not need to do the first and last items in this second list. Windows has the option to terminate your application, but it doesn’t always do it. In fact, I’ve been surprised by how long suspended applications remain in memory on my Surface RT. It’s rather frustrating if you’re trying to verify that your application works like it’s supposed to when it gets suspended and then evicted in a real-world scenario. (It’s easy to test this stuff from Visual Studio, but at some point you’ll want to manually verify that your app does the right thing outside of a test environment, and prompting a real-life post-suspend termination takes some effort.)
There’s a subtle variation on this—some of the work I’ve just described might be necessary even when your app remains in the foreground. If the user navigates to a different page within your application, you might want to save all your UI state so that you can restore things how they were if the user should return to the page—this involves the same work as the third step in each of the two lists above.
Consequently it’s common to have shared code that comes into play both when switching in and out of the app, and also when navigating between pages within the app.
Again, there’s a variation that can catch you out. In some situations, page objects will remain in memory as you navigate around, in which case it may not be necessary to do anything when the user returns to a page.
Before I talk about what each of the various events and methods does, and how they relate to the various tasks just described, it’s worth being aware that not all of the mechanisms are part of WinRT. Some come from the templates Visual Studio uses for new Windows Store projects. The features built into WinRT are: the page’s OnNavigatedFrom
and OnNavigatedTo
methods, the application object’s Suspending
and Resuming
events, and the core window’s Activated
and VisibilityChanged
events.
SaveState
and LoadState
are defined by the LayoutAwarePage
base class that Visual Studio adds to most Windows Store projects. (The very simplest project type doesn’t have this class.)
Just to confuse matters, although the page’s OnNavigatedFrom
and OnNavigatedTo
methods are intrinsic to WinRT, the Visual Studio templates change how they work—left to its own devices, WinRT will only call these for intra-app navigation, but with most of the Visual Studio templates, you’ll also find that OnNavigatedFrom
gets called during suspension, and that OnNavigatedTo
is called if your application is relaunched as a result of the user trying to return to it after Windows terminated it. Somewhat inconsistently, OnNavigatedTo
is not called if the user does exactly the same thing (goes away and then comes back) but Windows happened not to terminate your application in between.
Anyway, enough background…what do they all do, and what are they for?
I’ll work through the events and methods starting from a state when your application is running, working through to the point where the user has switched away. In a later post, I’ll describe what happens when the user returns.
The first thing you’ll probably see when the user switches away is the core window’s Activated event. If that sounds backwards, it’s because this one event is used for both activation and deactivation. In this case, the event’s WindowActivatedEventArgs
will have a WindowActivationState
property value of CoreWindowActivationState.D
eactivated
.
Deactivation is arguably the twitchiest of the notifications, because your application might not be going anywhere. All this tells you is that the focus is no longer within your application. That can happen simply because there are two applications on screen using the ‘snap’ feature that lets you run Windows Store apps side by side. If your application has the focus, but the user then taps in another app that’s also currently on screen, yours will be deactivated. But it doesn’t mean you’re going anywhere—your application is still there, and the user can easily tap it, at which point it’ll be reactivated.
In fact, your app can be deactivated even when it’s the only app on screen! If the user brings up the list of other apps by hitting WindowsKey+Tab, the keyboard focus goes into that list, and your application gets deactivated. (If the user decides not to switch applications, you’ll get reactivated when they dismiss the app list.)
So there’s not much to be done during deactivation. You might conceivably want to change some colours on screen to indicate that you no longer have the focus. If you show temporary popups of the kind that should go away promptly as soon as the user shows an interest in something else, deactivation can be a good moment to dismiss them. But you don’t need to start getting ready for your world to go away.
The first real clue that your app is about to leave the screen (typically because the user has just switched to a different app) is that the core window raises its VisibilityChanged event, and the EventVisibilityEventArgs
will return false from its Visible
property.
This is very likely to be an event you’ll want to handle, although you wouldn’t necessarily know that from the documentation. Far more attention is given to the Suspending
event I’ll be getting to shortly, but in a lot of cases handling visibility changes is a better bet, because you get this event the moment the user switches away from your application.
I’ve been working recently on an application that plays video, and the vast majority of advice I’d found on how to manage playback when the user switches in and out of your application talks about either suspension or activation. However visibility change events turned out to be a far better choice. (The problem incidentally, is that although the MediaElement
fades down the volume automatically when the user switches away from your app, inexplicably it pretends that playback continues, muted, in the background. So when the user switches back to your app, it will seem as though the video had been playing silently all along, because it will have advanced to the same point it would have reached if you’d stayed in the app and watched it. Not only is this the default, you can’t really turn it off—the only directly supported alternative is to arrange for audio to continue even when your app leaves the foreground. If you actually want the behaviour that most users will expect (which is what the built-in Windows 8 video player app does) you’ll need to write code to pause playback when the user switches away. And the VisibilityChanged
event is the only one that comes in at the right moment. The other popular suggestions just don’t work—if you pause on activation, you’ll end up stopping playback when your app hasn’t gone anywhere. And if you rely on Suspended
, you’ll find that it fires many seconds after the user left your app, so you’ll end up missing some content.)
Not only does this event come exactly when you want it, it might be the only notification that the user has left your app—there’s no guarantee that you’ll even see a Suspending
event, because the user might return to your app before Windows had a chance to suspend it. But you will always see visibility change events.
So in summary, this is a useful event: it is your only timely, dependable notification that the user has switched away from your application.
A few seconds after the user has displaced your app from the screen, Windows will usually decide to suspend it. This prevents applications that are not running from consuming CPU cycles, but by keeping apps suspended in memory, they can be restarted more swiftly than would be possible if applications were always terminated after switching away. The tricky thing about suspension is that it might be the last thing that ever happens to your app—once suspended, an app might never get restarted, and Windows may simply terminate it without further notice.
So the Suspending
event is important for one reason: it is your last opportunity to save state persistently. Apps will typically have two goals here: 1) don’t lose the user’s data, and 2) present the illusion that the application was there in the background all along when the user returns to it, even if Windows did in fact terminate it to free up some memory. So as well as saving user data (whatever that means for your application) you’ll typically want to store more superficial stuff like remembering which page the user was on, what was selected, where lists were scrolled to and so on. Obviously, you should prioritise substantial data over the more superficial stuff—save the user’s edits, notes, high score (or whatever the important user-generated information is in your app) first, then move onto the UI state, just in case you run out of time while saving the data. (You’re only allowed 5 seconds.)
All this work to preserve UI state may be wasted—it’s quite common for suspended applications to resume execution, because the user may switch back to them. (That’s the whole point of suspension.) And since, in that case, your application is reawakened with all its memory intact, it typically doesn’t need to load back in any of the state that it carefully wrote out, because it’s all still there in memory. But you don’t know at the moment of suspension what’s going to happen, so you have to assume the worst, and save everything.
So in summary, Suspending
is where you should ensure all user information has been saved, and then save whatever you need to be able to re-open the UI in the same state it’s in now. Be aware that you might not see this event, and if you do, it’ll typically be several seconds after the user switched away.
You might not necessarily handle the Suspending
event directly, because the templates provide you with this notification indirectly.
If you’re using the suspension manager and navigation infrastructure provided by all but the most basic of Visual Studio’s store app templates, the next thing to happen will be that the current page’s OnNavigatedFrom
method will be called. Left to its own devices, WinRT only calls this method when a Frame
navigates away from one page (just before navigating into the target page). This method is mainly concerned with navigation between pages within your app, so in general, you’d never expect to see this when a user switches away. Your app continues to be on the same page, as the user will see if she ever switches back. So there’s no sense in which your page is really being navigated away from. Nevertheless, you’ll see this method get called.
The reason it happens is that the Visual Studio templates generate a handler for the Suspending
event, and in that handler, they automatically persist the current navigation stack—they store a record not just of which page you’re on now, but also everything in the back/forward history for the frame. They do this by calling the Frame
class’s GetNavigationState
method, and that turns out to have the side effect of calling the current page’s OnNavigatedFrom
.
The rationale for this is that you’re going to want to do all the same work that you would have done if the user was navigating from this page to another within your app. For intra-app navigation, you’ll want to store away a record of superficial UI element state such as item selection and scrollbar positions, so that if the user returns to your page (by hitting the back button) it appears to be in the same state as before, even though it may be represented by a whole new object. You will want to do all that same work during suspension too, so that if you do get terminated, you are able to put the UI back into its previous state if the user does ever return to your app.
So the Frame
class ‘helpfully’ calls OnNavigatedFrom
when asked to persist its state, so that your page can save its state too. But this feels like a bit of a hack, because navigation isn’t really happening. Worse, it makes it hard to tell the difference between suspension and navigation. If you need to know which of those two operations caused the method to run, you’ll either need to modify the suspension code so that you can find out whether suspension is in progress, or you could handle the OnNavigatingFrom
method. (With intra-app navigation, OnNavigatedFrom
is preceded by a call to OnNavigatingFrom
, but the call to Frame.GetNavigationState
during suspension only causes the former to be called.) Either way, you’re relying on indirect cues.
The Visual Studio templates choose to wrap all this in a layer of abstraction, so in practice, you’ll often override not OnNavigatedFrom
, but SaveState
.
The LayoutAwarePage
base class that Visual Studio typically adds to store application projects defines a SaveState
virtual method. It calls this from its OnNavigatedFrom
, passing in a dictionary to which you can add data. Because OnNavigatedFrom
gets called in two scenarios—intra-app navigation and suspension—the same is true for SaveState
. And the information you pass to this dictionary is also returned in two scenarios: resumption, and intra-app navigation.
However, there’s one significant scenario in which you’ll never see this data again: if the user switches away from your application for long enough that the Suspending
event is raised, then you’ll see a call to SaveState
(thanks to the call to OnNavigatedFrom
triggered by the call to Frame.GetNavigationState
made by the suspension manager supplied by the Visual Studio template). If the user then returns to your application while it was still suspended in memory, your page will not be notified, and you don’t get handed this data back.
Arguably, you don’t need the data because the state you saved is all still in memory anyway, so there’s no need to try to reconstitute it. However, the subtle point here, which may cause difficulty, is that your page gets no notification whatsoever that the user has returned. This seems a little inconsistent. You do get notifications in the following scenarios: a) the user navigates to a different page within your application and then returns; b) the user switches away from your application, it gets suspended, then Windows terminates the process to free up some memory, then the user returns to the app, so Windows has to re-launch it, at which point the suspension manager will arrange to pass you back the state you saved. It seems a little weird that both these scenarios get handed back the data, but the one where the process happened to remain in memory, suspended, does not. The rationale is presumably that you only need to restore the state if you no longer have access to the page object, which would be the case either when navigating between pages (assuming the page wasn’t cached) or when the app gets terminated. When resuming from suspension, the current page object is still there, so a call to LoadState
would arguably be redundant.
So it does make sense, but the lack of symmetry here can be confusing at first.
Note that everything you put in the dictionary has to be serializable, because when SaveState
is called during suspension (as opposed to intra-app navigation) the information will be written to disk. If you use anything other than primitive data types, they’ll need to be marked with the [
DataContract
]
attribute to enable serialization.
SaveState
is mostly useful for storing superficial user interface state so that it can be reconstituted if the user returns to your application after the original UI objects are no longer available, either because you’re doing intra-app navigation and the page object is no longer there, or because your app got terminated after being suspended. There’s no guarantee that you’ll ever have the data you saved returned to you, and this event typically happens several seconds after the user switched away from your app. And if the user returns to your app fast enough (i.e., before Windows manages to suspend it) then you’ll never get this event. So this is not a good place in which to bring ongoing work (e.g., a game engine, or video playback) to a halt. As already discussed, visibility changes are much better for that sort of thing.
This has turned into a surprisingly long post, so I’ll save the second half of the story, resumption, until next time.