新型应用程序

Windows 应用商店应用程序的生命周期

Rachel Appel

 

Rachel AppelWindows 8 正在改变应用程序运行的方式和时间,并且您不免希望了解新应用程序的生命周期的细微差别,以便构建在每个点上都会产生预期反应的应用程序。符合 Microsoft 生命周期管理指导原则的应用程序将为用户提供更好的体验,尤其是在看重节省内存和电池的小型设备上。

应用程序设计

两个重要的设计概念是 Windows 应用商店应用程序的基础: 应用程序可在全屏、贴靠或填充模式下运行;并且应用程序必须快速响应,以便用户可以关注手头的内容而很少会分心。这两个原则确保当前正运行的应用程序从操作系统和用户获得所有可用资源,而这与 Windows 8 桌面应用程序或以前 Windows 版本上的应用程序不同,后者不得不共享这些资源。

所有 Windows 应用商店应用程序都具有四个状态: 未运行、正在运行、已挂起和已终止。在您启动某一应用程序时,该应用程序将运行。之后,根据用户或系统活动,该应用程序可在正在运行状态和已挂起状态之间转换。例如,如果用户从应用程序 A 切换到应用程序 B,则 Windows 会在一个短暂的延迟后挂起应用程序 A,并且将其移到后台。应用程序 A 保持在已挂起状态(直到用户切换回该应用程序或者 Windows 终止该应用程序),而应用程序 B 激活并且进入正在运行状态(如果该应用程序已处于内存中,则可能是从已挂起状态进入正在运行状态的)。如果用户返回到应用程序 A,Windows 只是将其唤醒,并且直到操作系统和应用程序A 都知道为止,它将一直处于正在运行状态。当然,然后就轮到应用程序 B 处于挂起状态了。

在某一应用程序处于已挂起状态时,其代码将不运行并且该应用程序按原样保持在内存中。实质上,该应用程序将被存入缓存并且在用户切换回时立即可供使用。但并非所有情况都是如此 — 如果您按照适当的过程执行,则可以运行后台任务,并且由于内存压力,Windows 可能会终止应用程序。图 1 阐释了应用程序如何在各个执行状态之间转移(有关详细信息,请参阅 bit.ly/TuXN9F)。

Windows 应用商店应用程序如何在各个执行状态之间转移
图 1 Windows 应用商店应用程序如何在各个执行状态之间转移

图 1 所示,一个应用程序可以频繁地在正在运行状态和已挂起状态之间转移。这个应用程序生命周期状态转换凸显了 Windows 应用商店应用程序和传统的桌面应用程序之间的差异。

Visual Studio 2012 包含大量的调试工具以及 Windows 模拟器 (bit.ly/QzBy5I),可用来管理应用程序生命周期操作。这很重要,因为您需要对应用程序生命周期事件作出适当的反应并且正确地处理状态转换,以便为最终用户带来快速响应体验 — 即 Windows 8 上应用程序设计的核心原则。通过适当地响应应用程序生命周期事件,您可以确保用户在整个应用程序生命周期中具有一致的体验。具体而言,这意味着节约资源以及只要需要就可以还原您的应用程序的状态。这可能需要将用户返回到她曾经在您的应用程序中所处的位置(例如向导中)、重新填充她正在使用的窗体的值或者返回到她阅读的上一篇文章。因为应用程序生命周期由用户在应用程序中的移动来控制,所以,需要对应用程序进行准备,以便立即对其状态执行检查点检查 — 只要应用程序获取挂起事件。

应用程序激活

WWAHost.exe 进程是执行 Windows 应用商店 JavaScript 应用程序的应用程序宿主。使用任何语言(例如 C#、Visual Basic 或 C++)编写的 XAML 应用程序运行该应用程序的相应可执行文件。无论采用哪种方式,所有应用程序都要经历激活,这具有许多促因:

  • 用户从磁贴启动某一应用程序。
  • 用户切换到某一已挂起的应用程序。
  • Windows 语言通过“搜索”或“共享目标”合约启动应用程序。
  • Windows 调用某个协议(URI 架构)关联 (bit.ly/QyzX04) 或者由于文件关联而启动某个应用程序。
  • Windows 调用一个扩展,例如文件打开选取器合约或联系人选择器。

激活发生的方式确定需要运行的代码。从某一磁贴、合约或协议启动将导致 Windows JavaScript 库 (WinJS) 应用程序触发已激活的事件,并且导致 XAML 应用程序触发 OnLaunched 事件。在这些事件中,您检查应用程序的之前状态以便采取相应操作。

如果应用程序正在从未运行状态转移到正在运行状态,则您需要加载新数据,因为这个转换指示该应用程序启动来自磁贴、超级按钮、协议或扩展。如果应用程序正在从已挂起状态返回,则您通常不需要执行任何操作;用户可以刚好从上一次停止的地方继续。如果应用程序正在从已终止状态转换为正在运行状态,则您需要重新加载数据并且在应用程序中导航到该用户最后所处的位置(除非自上次使用后已过了数月)。用于处理激活的代码显示在图 2 的 JavaScript 中以及图 3 的 C# 中。如代码中所示,您可以测试以前的执行状态以及 Windows 启动该应用程序的方式,因为 Windows API 具有针对这两种情形的一组便利的枚举。一旦您具有这些信息后,就可以根据需要重新填充数据。

JavaScript activated 和 XAML OnLaunched 事件都包含 args 参数,您可以查询该参数以便在激活前确定应用程序所处的状态。图 2图 3 显示激活的事件的示例。

图 2 JavaScript 应用程序激活代码

var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
app.addEventListener("activated", function (args) {
  if (args.detail.kind === activation.ActivationKind.launch) {
    if (args.detail.previousExecutionState !==
      activation.ApplicationExecutionState.terminated) {
      // TODO: This application has been newly launched.
      // Initialize your application here.
    } else {
      // TODO: This application has been reactivated from suspension.
      // Restore application state here.
    }
    if (app.sessionState.history) {
        nav.history = app.sessionState.history;
    }
    args.setPromise(WinJS.UI.processAll().then(function () {
      if (nav.location) {
          nav.history.current.initialPlaceholder = true;
          return nav.navigate(nav.location, nav.state);
      } else {
        return nav.navigate(Application.navigator.home);
      }
    }));
  }
});

图 3 C# 应用程序激活代码

async protected override void OnLaunched(LaunchActivatedEventArgs args)
  {
// Check whether the session data should be restored.
    if (args.PreviousExecutionState == 
      ApplicationExecutionState.Terminated)
    {
      // Here we've created a SuspensionManager class that
      // handles restoring session data from a file and
      // then gives access to that data through a Dictionary.
      await SuspensionManager.RestoreAsync();
// Retrieve the data for restore.                 
data = SuspensionManager.SessionState["savedData"];
    }
    // If not, use the app's default values
    else
            {
data = "Welcome";
    }
    Window.Current.Activate();
}

在激活过程中运行的代码必须在 15 秒内运行,否则,Windows 将终止该应用程序。 尽管这可能看起来有些严格,但在那么长的时间中阻止用户与 UI 交互的应用程序不是快速响应的应用程序。 根据 UseIt.com 对用户和响应时间的研究 (bit.ly/NWSumy),大多数用户都会放弃加载时间超过 10 秒的网站。 事实上,当页面加载时间超过 1 秒或 2 秒时用户就变得失望,许多人不会等 10 秒就早离开了。 如果您计划在 Windows 应用商店销售您的应用程序,则知道与用户有关的这个情况尤其重要,因为失望的用户会发布负面的应用程序评论,更别提您的应用程序甚至可能不会通过应用商店认证了(如果您的应用程序表现很差)。 (有关提交应用程序的更多信息,请参见 bit.ly/OxuEfu。) 仔细考虑要加载的数据以及何时加载十分重要。 幸运的是,Windows 运行时 (WinRT) 库通过提供一流的异步编程模型,便于快速、逐步加载数据 (bit.ly/NCx6gE)。

Windows.ApplicationModel.Activation 对象是所有 Windows 应用商店应用程序都可以访问的 Windows API 部分,与语言无关。 Activation 对象包含具有以下值的 activationKind 枚举:

  • Launch: 用户启动了应用程序或者点击了辅助磁贴。
  • Search: 用户想要使用该应用程序进行搜索。
  • ShareTarget: 该应用程序作为共享操作的目标激活。
  • File: 应用程序启动了此应用程序注册处理其文件类型的文件。
  • Protocol: 应用程序启动了此应用程序注册为处理其协议的 URL。
  • FileOpenPicker: 用户想要提取应用程序提供的文件或文件夹。
  • FileSavePicker: 用户想要保存文件并且选择了该应用程序作为位置。
  • CachedFileUpdater: 用户想要保存应用程序为其提供内容管理的文件。
  • ContactPicker: 用户想要提取联系人。
  • Device: 应用程序处理 AutoPlay。
  • PrintTaskSettings: 应用程序处理打印任务。
  • CameraSettings: 应用程序从连接的相机捕获照片或视频。

因为您的应用程序可能支持多个功能,例如共享和搜索,所以,activationKind 枚举允许您查看用户启动该应用程序的意图是什么,然后执行相应的代码。

管理挂起、终止和恢复

在某个用户切换到不同应用程序时,或者在设备休眠或进入睡眠状态时,Windows 将停止应用程序代码运行,但将该应用程序保持在内存中。 此挂起的原因在于,这可以尽量节约电池,并且尽量降低对用户未主动从其获取值的应用程序的性能影响。 在 Windows 将该应用程序从挂起或短暂终止中唤醒时,用户应该感觉就像该应用程序从未停止运行一样。 正确地处理生命周期事件可确保快速响应的设计。

Windows 通过引发 WinJS oncheckpoint 事件或 XAML OnSuspending 事件来开始挂起进程,在该进程中,您可以在应用程序进入已挂起模式前保存数据和用户信息。 您在这些事件中运行的任何代码都必须在 10 秒内完成,否则,Windows 将完全停止该应用程序。 与激活进程一样,此规则保持操作系统的整体运行状况稳定并且实现快速转换。 挂钩到 oncheckpoint 事件要求简单的函数定义:

app.oncheckpoint = function (args) {
  // Save app data in case of termination.
};

在 XAML 中,您使用 Application.Suspending 事件:

async void Suspending(Object sender,
  Windows.ApplicationModel.SuspendingEventArgs e) {
  // Save app data in case of termination.
}

在挂起期间,您需要保存用户的位置或应用程序中的滚动位置,释放文件句柄和连接,并且给数据加日期和时间戳。 您可以使用内置的 WinJS.Application.sessionState 或 XAML SuspensionManager 对象完成此任务。 您应该始终保存挂起事件中的用户信息和应用程序数据,因为 Windows 不会在它终止应用程序前进行通知。 这十分重要,因为终止可能会在多种条件下发生,例如在 Windows 需要释放内存或者在设备丧失(电池)电力时。

因此,代码应进行防备并且假定应用程序将终止。 这意味着您应该每次都将应用程序数据保存到 sessionState。 如果 Windows 确实终止了该应用程序,则数据将被保存下来并且可供重新填充。

恢复将在 Windows 将应用程序从挂起状态唤醒时发生。 大多数情况下,Windows 只是在用户切换回您的应用程序或重新启动它时恢复您的应用程序,并且您无需执行任何操作。 在挂起期间,应用程序只是处于内存中并且保持不变,因此,应用程序内容保持不变并且无需执行任何操作。

尽管大多数应用程序不需要在从已挂起状态还原时执行任何工作,但包含经常变化的数据(例如 RSS 源、股票或社交网络)的应用程序很适合于使用恢复事件。 因为恢复事件适合这几个特定情况,所以它处于 Windows.UI.WebUI.WebUIApplication 对象中,而不是与 oncheckpoint、onactivated 和其他同级的生命周期事件一起处于更常见的 WinJS.Application 对象中:

Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function () {
  // Repopulate data from sessionState.
});

您可以出于代码组织目的将恢复处理程序添加到 default.js 文件中靠近激活代码处。 通过在恢复时仅快速加载极少的数据,使您的应用程序快速响应。

后台任务和实时通信应用程序

尽管 UI 代码在应用程序处于已挂起模式下时不运行,但应用程序可以执行后台任务 (bit.ly/QyRvsU)、传输大型文件或检查是否有电子邮件。 某些实时通信应用程序(例如 IM 客户端)需要能够在消息到达时通知用户,而与应用程序执行状态无关。

后台任务是在应用程序在技术上未运行时基于某些限制定期运行的轻型类。 在应用程序保持在未运行状态时,这些后台任务在自己的沙盒或应用程序容器内运行。 JavaScript 后台任务在 WWAHost.exe 的新的单线程单元中运行,而非 JavaScript 后台任务在加载到应用程序的自己的线程单元内的进程中 .dll 中执行。 通过在这两个不同的单元中运行,允许后台任务独立于应用程序的 UI 运行,这将在用户返回到应用程序前保持已挂起状态。

除了能够在后台执行代码之外,这些应用程序还在锁屏上显示信息。 该锁屏包含一个轻型信息置于其上的背景图像,这些轻型信息包括时间、日期、电池状态和网络状态等。 此外,应用程序(具体来说,是正在后台运行的应用程序)可以在锁屏上显示信息。 因为保持可以轻松地领会锁屏十分重要,所以,可以显示的信息受到限制。 最多可以显示来自七个应用程序的锁屏提醒,并且来自单个应用程序磁贴的文本可以显示在锁屏上。 有关详细信息,请参阅 bit.ly/RsE7pj 上的锁屏概述。

遵循模型

作为 Windows 应用程序应用商店的开发人员,您需要关注各种问题,例如电池寿命和内存压力、在多种不同设备上运行您的应用程序,最重要的是,需要关注 UX。 如果您遵循规定的生命周期管理指导原则,则会发现实现目标将更容易。 此外,除了针对应用程序生命周期管理的技术原因外,如果您的应用程序响应迅速并且性能优异,则该应用程序在 Windows 应用商店中会得到更好的评价。

Rachel Appel 是 Microsoft 在纽约市的开发推广人员。 您可以通过网址为 rachelappel.com 的网站与她联系,或者从 rachel.appel@microsoft.com 通过电子邮件与她联系。 您还可以在 Twitter 上(网址为 twitter.com/rachelappel)留意她的最新更新。

衷心感谢以下技术专家对本文的审阅: Adam Barrus、Ben Betz、Arun Kishan、Michael Krause、Hari Pulapaka、John Sheehan 和 Ben Srour