win8开发应用之二:管理应用生命周期和状态(使用 C# 和 XAML 的 Metro 风格应用)

来源:互联网 发布:2016chinajoy数据 编辑:程序博客网 时间:2024/06/06 04:49
在 Windows 8 中,可以启动多个应用并在其中切换,无须担心降低系统速度或消耗电池电量。 这是因为系统会自动挂起(并且有时会终止)在后台正在为你运行的应用。设计良好的应用可以由系统挂起、终止以及重新启动,并且看起来似乎该应用一直在运行中。

了解操作方法:

  • 使用多种类型的本地存储保存状态
  • 下次启动应用时还原应用状态

应用生命周期简介

在我们返回到代码之前,我们来讨论一下应用的生命周期。激活标记着应用生命周期的开始。在任意给定时间点,应用未应用、正在运行或挂起。

应用可在用户离开它或 Windows 进入电量不足状态时挂起。 当应用挂起时,它继续驻留在内存中,以便用户可以快速且可靠切换到该应用。

当应用挂起时,它可以随时恢复运行。Windows 也可以随时终止该应用来为其他应用释放内存或节省电量。

当应用挂起然后恢复运行时,它看上去好像一直在运行。 如果应用被终止,它会停止运行并且从内存中卸载。Windows 会在挂起应用时通知应用,但不会在终止应用时提供其他通知。 这表示应用应处理挂起的事件,使用该事件保存其状态以及立即释放其独占资源和文件句柄。

 

 

步骤 1:使用 SuspensionManager

当我们使用部分 1:创建“Hello, world”应用中的“基本页”模板时,Microsoft Visual Studio 向 Common 文件夹中的项目添加多个文件。这些文件之一包含SuspensionManager 类。SuspensionManager 为帮助程序类,我们使用该类可简化应用的生命周期管理。该类可为你实现很多操作。它会保存和还原托管应用页面的Frame 的导航状态。在单页应用中,保存导航状态似乎不是非常重要,但在向应用中添加多个页面时该操作就变得非常重要了。它还为每个页面提供了保存并还原其状态的机会。SuspensionManager 序列化页面状态数据并将其写入应用本地存储中的 XML。

若要使用应用中的 SuspensionManager 类,首先需要注册主应用 Frame。完成操作后,SuspensionManager 可保存和还原导航状态,并且了解有关应用中每个页面的信息。在 App.xaml.cs/.vb 的OnLaunched 方法中创建Frame 之后,会立即进行注册。

使用 SuspensionManager

  1. 调用 SuspensionManager.RegisterFrame 方法可注册根 Frame。     
      HelloWorld.Common.SuspensionManager.RegisterFrame(rootFrame, "appFrame");


       2. 移动 rootFrame 的声明。

           在还原 Frame 的状态之前需要进行注册,以便将用于声明和注册 rootFrame 的代码移至还原应用状态的代码之前。

      

      

  /// <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)        {            // Do not repeat app initialization when already running, just ensure that            // the window is active            if (args.PreviousExecutionState == ApplicationExecutionState.Running)            {                Window.Current.Activate();                return;            }            // Create a Frame to act as the navigation context and navigate to the first page            var rootFrame = new Frame();            HelloWorld.Common.SuspensionManager.RegisterFrame(rootFrame, "appFrame");            if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)            {                //TODO: Load state from previously suspended application            }            if (!rootFrame.Navigate(typeof(MainPage)))            {                throw new Exception("Failed to create initial page");            }            // Place the frame in the current Window and ensure that it is active            Window.Current.Content = rootFrame;            Window.Current.Activate();        }


步骤 2:保存应用状态

我们需要保存哪些数据以及应何时保存数据?此时,用户可以在页面中更改的唯一事项为其名称。用户还可以单击“Say "Hello"”按钮来生成个性化问候。

通常,在应用中管理的两种数据为:用户数据和会话数据。用户数据保留在会话中且用户必须始终可以访问这些数据。在应用中,nameInputTextBoxText 为用户数据。在应用的整个生命周期内始终不断保存重要的用户数据。由于应用最多只有 5 秒钟来运行挂起事件处理程序中的代码,因此你需要确保当应用挂起时将重要应用数据保存到永久性存储中。

Windows 提供了 Windows.Storage.ApplicationData 对象,有助于我们管理应用数据。 此对象的 LocalSettings 属性会返回 ApplicationDataContainer。我们可以使用此本地 ApplicationDataContainer 来存储会话中保留的用户数据。当用户在本地ApplicationDataContainer 中键入用户名时,我们在其中存储该名称。

会话数据为临时数据,它与应用中用户的当前会话相关。会话会在以下情形下结束:用户使用“任务管理器”关闭应用,重新启动计算机,注销计算机,或使用关闭手势或 Alt + F4 来关闭应用。 在应用中,greetingOutputTextBlockText 为会话数据。仅当 Windows 挂起和终止应用时才还原该数据。我们需要保存应用 Frame 的导航状态,以便应用可以还原到其所在的同一页面,以及以便 SuspensionManager 了解要还原其状态的页面。我们还需要保存页面自身的状态。此为我们保存greetingOutput 文本的位置。我们使用SuspensionManager 类来保存 Application.Suspending 事件处理程序中的会话状态。

(有关保存状态的其他方法的详细信息,请参阅管理应用数据和有效使用状态。)

只要持续的用户数据在应用程序中有意义时都会保存该数据。此时,我们处理 TextBox.TextChanged 事件并当用户输入用户名时保存该用户名。

Hh986968.wedge(zh-cn,WIN.10).gif保存用户数据

  1. 在可扩展应用程序标记语言 (XAML) 或设计视图中,选择已添加到 MainPage.xaml 的 nameInput TextBox
  2. 在“属性窗口”中,单击“事件”按钮 (“事件”按钮)。

    提示  如果你没有看到“属性窗口”,则按 Alt+Enter 将其打开。

  3. 在事件列表中找到 TextChanged 事件。在事件的文本框中,键入处理 TextChanged 事件的函数名称。对于本示例,请键入 "NameInput_TextChanged"。
  4. 按 Enter。事件处理程序方法在代码编辑器中创建和打开,因此你可以添加在事件出现时执行的代码。
  5. 向在代码隐藏页面中的事件处理程序添加代码。在事件处理程序中,保存 localSettings 中的 nameInput 文本。    
      private void InputName_TextChanged(object sender, TextChangedEventArgs e)        {            ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;            localSettings.Values["userName"] = nameInput.Text;        }

       6.按 F5 可构建并运行应用。当你在文本框中输入名称时会保存该名称。

App.xaml.cs/.vb 文件包含 Application.Suspending 事件的处理程序。Windows 即将挂起应用时会调用此事件处理程序。 这是我们保存应用状态以防该应用被终止的时机。我们使用SuspensionManager 类来简化会话状态保存过程。该类会保存应用的导航状态并为你提供保存活动页面会话状态的机会。

Hh986968.wedge(zh-cn,WIN.10).gif保存会话状态

  1. 在 App.xaml.cs/.vb 中,向 OnSuspending 方法签名添加 async 关键字。

    有关异步编程的详细信息,请参阅快速入门:使用 await 运算符进行异步编程。

  2. 调用 SuspensionManager.SaveAsync 方法。

    调用 SaveAsync 可保存 Frame 的导航状态,然后为 Page 提供保存其内容的机会。

 /// <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>        private async void OnSuspending(object sender, SuspendingEventArgs e)        {            var deferral = e.SuspendingOperation.GetDeferral();            //TODO: Save application state and stop any background activity            await HelloWorld.Common.SuspensionManager.SaveAsync();            deferral.Complete();        }


       3. 在 MainPage.xaml.cs/.vb 中,向 SaveState 方法添加代码可保存页面状态。

     SuspensionManager 类会序列化 pageState 字典并将其保存到 XML 文件。在pageState 中保存的数据仅为此会话保存。此处保存greetingOutput 文本。

           保存 localSettings 中的 nameInput 文本。此数据保存在会话中。

protected override void SaveState(Dictionary<String, Object> pageState)        {            pageState["greetingOutputText"] = greetingOutput.Text;            // The user name is already saved, so we don't need to save it here.        }

       4. 按 F7 可确保应用构建且没有错误。

步骤 3:还原应用状态

        之前,我们看到 App.xaml.cs/.vb 文件包含处理应用激活的代码。可以使用多种不同的方法激活应用。此时我们看看启动激活和 OnLaunched 方法。

只要应用未运行,该应用就会被启动,然后用户激活该应用。当应用启动时,Windows 显示应用的一个初始屏幕。

我们来了解处理应用激活的 App.xaml.cs/.vb 中的代码。代码定义了 OnLaunched 方法重写。 仅当激活为 Launch 激活时才执行此方法中的代码。(你可以替代其他方法来处理其他种类的激活,但此处我们不执行该操作。有关详细信息,请参阅应用程序生命周期。)

首先,代码会检查以前的执行状态以查看应用是否已在运行。如果在运行,则仅激活当前窗口。

protected override void OnLaunched(LaunchActivatedEventArgs args)        {            // Do not repeat app initialization when already running, just ensure that            // the window is active            if (args.PreviousExecutionState == ApplicationExecutionState.Running)            {                Window.Current.Activate();                return;            }


如果应用未运行,则代码会创建 Frame 来托管应用页面。向 SuspensionManager 注册 Frame。此为我们在步骤 1 中添加的代码。

// Create a Frame to act as the navigation context and navigate to the first page            var rootFrame = new Frame();            HelloWorld.Common.SuspensionManager.RegisterFrame(rootFrame, "appFrame");

接下来,代码会检查以前的执行状态以查看上次应用如何关闭。如果以前的执行状态为 terminated,则该状态表示上次应用运行,Windows 成功挂起应用,然后终止该应用。在这种情形下,需要还原应用状态。

如果应用未被 Windows 终止,则代码将其视为应用第一次启动。

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

最后,代码会调用 Frame.Navigate 方法来转至 MainPage,并且激活窗口。由于将添加代码来还原应用的导航状态,因此仅当导航状态未被还原时才修改此代码来调用Frame.Navigate

            if (!rootFrame.Navigate(typeof(MainPage)))            {                throw new Exception("Failed to create initial page");            }            // Place the frame in the current Window and ensure that it is active            Window.Current.Content = rootFrame;            Window.Current.Activate();        }


 

Hh986968.wedge(zh-cn,WIN.10).gif还原应用状态

  1. 在 App.xaml.cs/.vb 中,向 OnLaunched 方法签名添加 async 关键字。
protected async override void OnLaunched(LaunchActivatedEventArgs args)


       2.   如果应用被终止,则调用 SuspensionManager.RestoreAsync 方法。

调用 RestoreAsync 会还原 Frame 的导航状态,然后为 Page 提供还原其内容的机会。

 if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)            {                //TODO: Load state from previously suspended application                await HelloWorld.Common.SuspensionManager.RestoreAsync();            }

        3.  仅当应用的导航状态未被还原时才修改导航至 MainPage 的代码。

            if (rootFrame.Content == null)            {                // When the navigation stack isn't restored, navigate to the first page.                if (!rootFrame.Navigate(typeof(MainPage)))                {                    throw new Exception("Failed to create initial page");                }            }


       4.  在 MainPage.xaml.cs/.vb 中,向 LoadState 方法添加代码可还原页面状态。

              1.  首先,检查以查看 pageState 字典是否存在且是否具有名为 greetingOutputText 的密钥。

   // Restore values stored in session state.            if (pageState != null && pageState.ContainsKey("greetingOutputText"))            {                greetingOutput.Text = pageState["greetingOutputText"].ToString();            }