Handling Suspension and Termination in Windows Store Apps – Boredom Challenge Day 14

Standard

It is not uncommon, especially in mobile app development, for our apps to be suspended or terminated in order to preserve the limited resources of a mobile device. Naturally, we would need to handle these situations in our apps, since there might be some final actions we need to do (such as saving our game’s progress, rolling back a not wholly completed calculation, or storing the contents of a half-filled form) before our app kicks the bucket. We would also need to handle the resuming in order to recover from suspension and termination.

Since Windows 8 emphasizes on being a mobile and power-saving OS, depending on the resources our app could get suspended when a user switches to another app or to desktop. Also, if the resources are low, the OS may deem it fit to outright terminate any suspended apps. Here’s a fancy diagram showing the Windows 8 app life cycle.

1

Apart from the power saving features of the OS, there is one more reason that makes it a good idea to handle suspension and termination in our apps, and this reason is the fact that there are no exit buttons. Our users can just close our apps with a signle flick gesture. We have no chance to give any warnings or prompts to our users like “Are you sure you want to exit?” or “Unsaved changes will be lost” and so on. So, you were uploading a file in the backround, or maybe you haven’t reflected a change you made in the app to the database yet. Well, tough luck, the user has just closed your app. Since the user (rightfully) doesn’t need to care about such things, it is our job to ensure the app overcomes these situations.

However, as you will see, it is quite easy to implement this feature, and in this article we will make a simple Windows Store app that handles suspension, termination and resuming.

Before creating the app, I’ll give a small explanation on how this works. Our app gets notified when it is about to be suspended or resumed, but it is not notified when it will be terminated. Therefore, we need to treat the suspend event like a termination. Also, from the user’s point of view the app may look like it is closed instantly, but actually the operating system gives us 5 seconds to wrap things up.

Ok then, let’s just create a blank Windows Store app and add two TextBlocks to the MainPage.xaml, one to show the status of the app and one to show the data saved on suspend.

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock x:Name="TextBlockStatus" HorizontalAlignment="Left" Margin="100,100,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="15"/>
        <TextBlock x:Name="TextBlockValue" HorizontalAlignment="Left" Margin="100,123,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="15"/>
    </Grid>

We’ll do the handling of suspension and resuming in App.xaml.cs:

using System.Threading.Tasks;
using Windows.Storage;
    sealed partial class App : Application
    {
        public string AppStatus = String.Empty;
        public string SavedData = String.Empty;
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
            this.Resuming += App_Resuming;
        }

        void App_Resuming(object sender, object e)
        {
            AppStatus = "Resuming from suspension.";
            SavedData = ApplicationData.Current.LocalSettings.Values["data"].ToString();
            ((Window.Current.Content as Frame).Content as MainPage).Refresh();
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            if (ApplicationData.Current.LocalSettings.Values["data"] != null)
            {
                SavedData = ApplicationData.Current.LocalSettings.Values["data"].ToString();
            }
            switch (args.PreviousExecutionState)
            {
                case ApplicationExecutionState.Suspended:
                    {
                        AppStatus = "Resuming from suspension.";
                        break;
                    }
                case ApplicationExecutionState.Terminated:
                    {
                        AppStatus = "Resuming from termination.";
                        break;
                    }
                case ApplicationExecutionState.ClosedByUser:
                    {
                        AppStatus = "Last session was closed by user.";
                        break;
                    }
                case ApplicationExecutionState.NotRunning:
                    {
                        AppStatus = "Launching for the first time.";
                        break;
                    }
            }

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
                {
                    throw new Exception("Failed to create initial page");
                }
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        async private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            await SaveAppData();
            deferral.Complete();
        }

        async private Task SaveAppData()
        {
            await Task.Factory.StartNew(() => ApplicationData.Current.LocalSettings.Values["data"] = DateTime.Now.ToString());
            return;
        }
    }

Here, we have two strings called AppStatus and SavedData, and you can guess what they’ll store from their names. OnSuspending function is called when the app is about to be suspended, and to demonstrate it we are saving the current time to the local storage. App_Resuming is called when the app is resumed from suspension (we’ll add the Refresh function at the next step). And finally, in OnLaunched, we check the previous execution state of the app to determine if it was closed by the user, suspended, terminated, or if it is being run for the first time.

Keep in mind that there are some exceptional cases. For example, if the user presses Alt+F4 to close the app, it will still be closed normally and the app status will be “closed by the user”. But if something wrong happens and the process is killed (via Task Manager, for example), there won’t be a previous execution state and our code will say “the app is running for the first time”. So if you wish to check if your app is really running for the first time, this is not a good way.

Finally we’ll add the following code in MainPage.xaml.cs to refresh the UI with the saved data and app status:

        public MainPage()
        {         
            this.InitializeComponent();
            this.Loaded += MainPage_Loaded;
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            TextBlockStatus.Text = ((App)Application.Current).AppStatus;
            TextBlockValue.Text = ((App)Application.Current).SavedData;
        }

        public void Refresh()
        {
            TextBlockStatus.Text = ((App)Application.Current).AppStatus;
            TextBlockValue.Text = ((App)Application.Current).SavedData;
        }

Before running our app, keep in mind that Visual Studio does not allow our app to be suspended when debugging. To test our suspend and resume code, we can use the suspend and resume buttons in Visual Studio (shown below). Also, if you close the app by stopping the debugging session (Red Button – Shift+F5), this will kill the process and won’t run the suspension code.

3

Now go ahead and run the app. You can close the app yourself, use the Suspend and Resume for suspension and resuming, or use Suspend and Shutdown for termination. They will look like this:

4

5

6

7

Simple, right? 🙂

You can get the source code from here.

Thank you for reading.

Advertisements

2 thoughts on “Handling Suspension and Termination in Windows Store Apps – Boredom Challenge Day 14

  1. Amol

    Hi.
    I got this tutorial. Thanks for your explanation.

    But how do we handle custom classes. Suppose I created a custom class and then pass that custom class object via frame.navigate and then i suspend the app.

    I tried this but the suspension manager fails in this case.
    How do we solve this problem? Can you show us an example !!

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s