Windows 10 2015 年特别版

第 30 卷,第 11 期

此文章由机器翻译。

应用生命周期 - 通过后台任务和扩展执行使应用处于活动状态

通过 Shawn Henry |Windows 2015 年

它用于将应用程序的生命周期很容易。当用户启动应用程序时,它无法疯狂的系统上运行: 消耗资源、 弹出窗口,并通常执行它而不考虑其他人高兴。如今,事情要更棘手。在移动优先世界中,应用程序被限制为定义的应用程序生命周期。此生命周期指定当应用程序可以运行,但大多数情况下它不能 — 如果应用程序不是当前用户执行的操作,它不允许运行。

在大多数情况下,这是很好 — 用户知道已应用程序不是在消耗能量或采用削弱性能。对于应用程序,操作系统什么一直都很好的做法,应用程序进入停止稳定状态在不活动的使用时强制实施。原因在于应用程序模型中通用 Windows 平台 (UWP) 需要从最低最终设备如电话和物联网 (IoT) 设备扩展到功能最强大的台式计算机和 Xbox 尤其重要。

对于复杂的应用程序,现代应用程序模型可能看上去限制性刚开始,但正如本文将介绍,有几种方法的应用程序可以展开框并运行 (完成的任务和传递通知),甚至当不在前景中。

应用程序生命周期

在传统 Win32 和.NET 桌面开发中,应用程序通常是在两种状态之一:"正在运行"或"未运行"和大多数情况下它们正在运行。这似乎很明显,但思考的即时消息等 Skype 应用程序或类似 Spotify 的音乐应用: 用户启动它、 执行某些操作 (发送消息或搜索音乐),则响起和执行其他操作。同时,Skype 位于在等待消息进入,后台并且 Spotify 保持播放音乐。这与花费的大部分时间中的状态不运行的现代应用 (例如 Windows 商店应用程序基于 UWP),完全不同。

Windows 应用程序都在三种状态之一: 正在运行、 挂起或未运行,如中所示 图 1。当用户启动 Windows 应用程序时,例如通过开始菜单上的磁贴上点击应用程序被激活并进入运行状态。只要用户与应用程序进行交互时,它将保持处于运行状态。如果用户导航离开应用程序或将其降至最低,激发的挂起的事件。这是应用程序进行序列化时它已恢复或重新激活,如应用程序或部分填充扩展窗体数据的当前页它可能需要的任何状态的机会。当应用程序处于挂起状态时,其进程和线程由 Windows 挂起,不能执行。当用户导航回该应用程序时,应用程序线程会被解冻和应用程序恢复运行状态。

Windows 应用程序生命周期
图 1 Windows 应用程序生命周期

如果应用程序处于挂起的状态,但是它使用 (通常是内存) 的资源,则需要用作其他用途,如运行另一个应用程序,该应用程序移到未运行状态。

从应用程序执行的角度看,挂起和未运行状态之间的区别是是否允许该应用程序驻留在内存中。当只是挂起应用程序时,它的执行被冻结,但所有其状态信息保留在内存中。当应用程序未运行时,它从内存中删除。当从移动应用程序挂起到未在运行时,会不触发任何事件,因为很重要的应用程序进行序列化所有它从内存中,所需的状态信息以防未在运行从重新激活它。

图 2 显示发生的资源使用情况与应用程序生命周期的转换。应用程序激活时,它开始占用内存,通常达到相对稳定比尔。当挂起应用程序时,其内存使用量通常出现故障 — 缓冲区和使用的资源的发行,和 CPU 占用率变为零 (由操作系统强制执行)。当在移动应用程序从挂起到未运行时,内存和 CPU 的使用将转到零,再次由操作系统强制执行。

应用程序生命周期和资源使用情况
图 2 应用程序生命周期和资源使用情况

大量的 RAM 和大型页文件的许多桌面系统上它并不常用 — 但不是不可能 — 从内存中,但这种转换中删除的应用程序是在移动设备和其他资源受限的设备上更常见。为此,务必测试 UWP 应用程序在各种设备上。Windows 仿真程序附带使用 Visual Studio 会非常有用对于这一点 ;它允许开发人员能够与目标设备作为小 512 MB 的内存。

扩展的执行

我所述的应用程序生命周期是有效 — 如果它们不会使用,应用程序不占用资源 — 但它会导致哪些应用程序需求不能完成其中的方案。例如,社会或通信的应用程序的一个典型用例是登录并同步一组云数据 (联系人、 源、 对话历史记录和等)。与基本应用程序生命周期所述,若要下载联系人、 用户需要保持该应用程序打开和在前台中的全部时间 ! 另一个示例可能是导航或适用性的应用程序需要跟踪用户的位置。只要用户切换到不同的应用程序或将设备置于其 pocket 时,这将不再起作用。需要有一种机制以允许应用程序运行较长时间。

通用 Windows 平台引入了扩展的执行来帮助进行这些类型的情况的概念。有两种情况下可以使用扩展的执行的位置:

  1. 在应用程序处于运行状态时的正则前台执行期间的任何时刻。
  2. 该应用程序接收到挂起的事件后 (是操作系统以将该应用程序移到挂起状态) 在应用程序的挂起事件处理程序。

这两种情况的代码是相同的但应用程序的行为有点以不同的方式在每个。在第一种情况下,应用程序处于运行状态,即使发生 (例如导航离开应用程序的用户) 的程序通常会触发挂起的事件。执行扩展在生效时,应用程序将永远不会收到挂起事件。当释放该扩展插件时,该应用程序将再次变为适合挂起。

与第二个用例,如果应用程序转换到挂起状态,它将保留在挂起状态的扩展的时间。该扩展插件到期后,应用程序将进入挂起的状态无进一步通知。

图 3 演示如何使用扩展的执行来扩展应用程序的 uspending 状态。首先,新 ExtendedExecutionSession 创建并附带的原因和说明。这两个属性用于分配正确的资源设置为该应用程序 (即量的内存、 CPU 和允许它的执行时间) 以及公开有关应用程序在后台执行的操作信息提供给用户。然后,应用程序挂接如果称为 Windows 不再可以支持扩展,例如,如果另一个高优先级的任务如前台应用程序或传入的 VoIP 呼叫,需要资源一个吊销事件处理程序。最后,应用程序请求该扩展插件,并且如果成功,将从开始保存操作。如果拒绝该扩展插件,该应用程序将在执行挂起操作尚未收到的扩展功能,像常规的挂起事件。

图 3 扩展 OnSuspending 处理程序中的执行

private async void OnSuspending(object sender, SuspendingEventArgs e)
{
  var deferral = e.SuspendingOperation.GetDeferral();
  using (var session = new ExtendedExecutionSession())
  {
    session.Reason = ExtendedExecutionReason.SavingData;
    session.Description = "Upload Data";
    session.Revoked += session_Revoked;
    var result = await session.RequestExtensionAsync();
    if (result == ExtendedExecutionResult.Denied)
    {
      UploadBasicData();
    }
    // Upload Data
    var completionTime = await UploadDataAsync(session);
  }
  deferral.Complete();
}

图 4 显示这对应用程序生命周期的影响 ; 将对其进行比较 图 2。当应用程序获取挂起事件时,它开始释放或序列化的资源。如果应用程序确定它需要更多时间,并采用一个扩展,它将一直处于挂起状态撤消该扩展插件。

扩展的执行期间的资源使用情况
图 4 在扩展的执行过程的资源使用情况

情况 2 中前面所述,执行扩展无需请求仅在挂起的处理程序 ;它可以在任何时刻在应用程序处于运行状态时请求。这可用于当在应用程序知道事先它将需要与前面所述导航布局应用程序可以继续在后台运行。代码非常类似于前面的示例和中所示 图 5

图 5 扩展在正则执行过程的执行

private async void StartTbTNavigationSession()
{
  using (var session = new ExtendedExecutionSession())
  {
    session.Reason = ExtendedExecutionReason.LocationTracking;
    session.Description = "Turn By Turn Navigation";
    session.Revoked += session_Revoked;
    var result = await session.RequestExtensionAsync();
    if (result == ExtendedExecutionResult.Denied
    {
      ShowUserWarning("Background location tracking not available");
    }
    // Do Navigation
    var completionTime = await DoNavigationSessionAsync(session);
  }
}

图 6 显示了这种情况下的应用程序生命周期。同样,它也是非常相似。不同之处在于时执行扩展被吊销,, 应用程序将可能迅速转变通过挂起状态进入未运行状态。这是因为在这种情况下,仅由于资源不足,仅通过释放资源可以缓解 (即,从内存中删除该应用程序) 的情况通常撤消该扩展插件。

扩展的执行期间的资源使用情况
图 6 在扩展的执行过程的资源使用情况

后台任务

没有应用程序可以在后台中运行的另一种方法,这就是作为后台任务。后台任务是在应用程序中单独实现 IBackgroundTask 接口的组件。这些组件可以没有重量级 UI 框架的情况下执行并 (尽管它们也可以运行进程内与主应用程序可执行文件) 通常执行的单独进程中。

激发相关联的触发器时执行后台任务。触发器是系统事件可以激发并激活应用程序,即使应用程序未运行。例如,可以使用特定时间间隔内 (说,每隔 30 分钟) 生成 TimeTrigger、 应用程序的后台任务将在这种情况下激活时激发该触发器每隔 30 分钟。有许多支持的 Windows,包括这些背景触发器类型的触发器类型: TimeTrigger、 PushNotificationTrigger、 LocationTrigger、 ContactStoreNotificationTrigger、 BluetoothLEAdvertisementWatcherTrigger、 UserPresent、 InternetAvailable 和 PowerStateChange。

使用后台任务是一个三步骤过程: 该组件需要创建,然后在应用程序清单中声明并随后在运行时进行了注册。

通常在单独的 Windows 运行时 (WinRT) 组件项目中与 UI 项目相同的解决方案中实现后台任务。这允许在一个单独的进程,减少开销由组件所需的内存中激活的后台任务。所示的简单实现 IBackgroundTask 图 7。IBackgroundTask 是一个简单的界面,用于定义只是一种方法运行。这是在后台任务触发器被触发时调用的方法。方法的唯一参数是一个包含有关激活 (例如,使用 toast 通知的操作相关联的推送通知的负载) 和事件处理程序来处理取消等的生命周期事件的上下文的 IBackgroundTaskInstance 对象。当 Run 方法完成后时,将终止的后台任务。为此,就像前面显示的挂起处理程序中一样是一定要使用的延迟对象 (还关闭 IBackgroundTaskInstance 悬挂) 如果您的代码是异步的。

图 7 BackgroundTask 实现

public sealed class TimerTask : IBackgroundTask
{
  public void Run(IBackgroundTaskInstance taskInstance)
  {
    var deferral = taskInstance.GetDeferral();
    taskInstance.Canceled += TaskInstance_Canceled;
    await ShowToastAsync("Hello from Background Task");
    deferral.Complete();
  }
  private void TaskInstance_Canceled(IBackgroundTaskInstance sender,
    BackgroundTaskCancellationReason reason)
  {
    // Handle cancellation
    deferral.Complete();
  }
}

在应用程序清单中,还必须注册的后台任务。此注册指示 Windows,但是触发器类型、 入口点和可执行该任务的主机,如下所示:

<Extensions>
  <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTasks.TimerTask">
    <BackgroundTasks>
      <Task Type="timer" />
    </BackgroundTasks>
  </Extension>

这也可以不深入探讨 XML 通过使用 Visual Studio 附带在清单设计器的情况下完成。

最后,还必须在运行时,注册该任务并显示了 图 8。此处我使用 BackgroundTaskBuilder 对象来注册该任务将激发每隔 30 分钟 TimeTrigger Internet 是否可用。这是触发器的理想的为诸如更新磁贴或定期同步少量数据之类的常见操作类型。

图 8 后台任务注册

private void RegisterBackgroundTasks()
{
  BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
  builder.Name = "Background Test Class";
  builder.TaskEntryPoint = "BackgroundTaskLibrary.TestClass";
  IBackgroundTrigger trigger = new TimeTrigger(30, true);
  builder.SetTrigger(trigger);
  IBackgroundCondition condition =
    new SystemCondition(SystemConditionType.InternetAvailable);
  builder.AddCondition(condition);
  IBackgroundTaskRegistration task = builder.Register();
  task.Progress += new BackgroundTaskProgressEventHandler(task_Progress);
  task.Completed += new BackgroundTaskCompletedEventHandler(task_Completed);
}

后台任务的最大优势也可以是其大麻烦: 由于在后台 (在用户可能需要执行一些重要在前台或该设备处于待机状态时) 运行后台任务,它们是严格的限制内存和他们可以使用的 CPU 时间量。例如,后台任务注册中 图 8 将仅为 30 秒对运行和不能使用超过 16 MB 的内存在设备上有 512 MB 内存 ; 内存上限进行缩放,在设备上的内存量。在开发和实现后台任务时, 务必考虑到这一点。后台任务应进行测试的各种设备,尤其是低端设备、 之前发布的应用程序。有几个要同时请注意其他事项:

  • 如果 Battery Saver 是可用并处于活动状态 (通常当电池是低于费用阈值),后台任务不能运行,直到电池过去的电池电量节省阈值重新充电。
  • 在以前版本的 Windows 中,将"固定"到锁之前他们之所以能够执行在后台,且在某些情况下,所需的应用程序没有可能是已注册的设备级的后台任务的最大数量。这不再是这种情况在 Windows 10,但应用程序必须始终调用 BackgroundExecutionManger.RequestAcessAsync 来声明其意图在后台中运行。

不要联系同步的更好方式

有许多后台操作可完成与任一扩展执行或通过后台任务。通常,是更好的做法尽可能使用后台任务 — — 它们更可靠、 更高效。

例如,上文所述的方案 — 应用程序首次启动时同步数据 — 还可以进行,并且更高效地通过后台任务,在这种情况下使用触发器不熟悉的 Windows 10: 应用程序触发器。

(如 DeviceUseTrigger) ApplicationTrigger 属于一种特殊的类的直接从应用程序的前景部分触发的触发器。这是通过显式调用 RequestAsync 触发器对象上。ApplicationTrigger 是对于应用程序要在应才有机会在后台继续进行如果该应用程序不再位于前台,而该操作不依赖于紧密耦合的前景前台启动操作的情况下特别有用。下面演示举例说明如何可以代替大多数情况下扩展执行 ApplicationTrigger 任务:

var appTrigger = new ApplicationTrigger();
var backgroundTaskBuilder = new BackgroundTaskBuilder();
backgroundTaskBuilder.Name = "App Task";
backgroundTaskBuilder.TaskEntryPoint = typeof(AppTask).FullName;
backgroundTaskBuilder.SetTrigger(appTrigger);
backgroundTaskBuilder.Register();
var result = await appTrigger.RequestAsync();

总结

本文提供 Windows 10 中的应用程序生命周期和后台执行的概述,并引入了几个新的应用程序可用于在后台运行的机制: 扩展执行和后台任务。后台任务是更好的选择为低端和内存约束设备 (如电话),而扩展的执行是更适用于更高端设备 (如桌面 Pc)。


Shawn Henry是通用的 Windows 平台构建团队的高级项目经理。与他联系在 Twitter 上关注: @shawnhenry

感谢以下的微软技术专家对本文的审阅: Anis Mohammed Khaja Mohideen 和 Abdul Hadi Sheikh
Abdul Hadi Sheikh 是通用的 Windows 平台团队的软件开发人员

Anis Mohammed Khaja Mohideen 是通用的 Windows 平台团队的高级软件开发人员