使用扩展执行来推迟应用挂起Postpone app suspension with extended execution

本文将为你展示当应用挂起时如何借助扩展执行进行推迟,从而让你的应用在最小化时或在锁屏界面中也可以运行。This article shows you how to use extended execution to postpone when your app is suspended so that it can run while minimized or under the lock screen.

当用户最小化或者离开应用时,应用会进入挂起状态。When the user minimizes or switches away from an app it is put into a suspended state. 其内存会保留,但是其代码不运行。Its memory is maintained, but its code does not run. 对于具有可视用户界面的所有操作系统版本均是如此。This is true across all OS Editions with a visual user interface. 有关应用何时挂起的更多详细信息,请参阅应用程序生命周期For more details about when your app is suspended, see Application Lifecycle.

某些情况下,应用在用户离开后或最小化时可能需要保持运行,而不是挂起。There are cases where an app may need to keep running, rather than be suspended, when the user navigates away from the app, or while it is minimized. 例如,即使用户离开去使用其他应用,计步应用也需要保持运行并跟踪步数。For example, a step counting app needs to keep running and tracking steps even when the user navigates away to use other apps.

如果应用需要保持运行,操作系统可以让它保持运行,或者它也可以请求保持运行。If an app needs to keep running, either the OS can keep it running, or it can request to keep running. 例如,在后台播放音频时,如果按照后台媒体播放的以下步骤进行操作,操作系统可以保持应用运行更长时间。For example, when playing audio in the background, the OS can keep an app running longer if you follow these steps for Background Media Playback. 否则,你必须多次手动请求。Otherwise, you must manually request more time. 你用来执行后台执行的时间可能只有几分钟,但你必须随时准备处理被吊销的会话。The amount of time you may get to perform background execution may be several minutes but you must be prepared to handle the session being revoked at any time. 当应用在调试程序下运行时,这些应用程序生命周期时间限制会被禁用。These application lifecycle time constraints are disabled while the app is running under a debugger. 出于这一原因,在测试推迟应用挂起的扩展执行和其他工具时,一定不要使用调试程序运行;或者,也可以借助 Visual Studio 中提供的生命周期事件进行测试。For this reason it is important to test Extended Execution and other tools for postponing app suspension while not running under a debugger or by using the Lifecycle Events available in Visual Studio.

创建 ExtendedExecutionSession 来多次请求在后台完成操作。Create an ExtendedExecutionSession to request more time to complete an operation in the background. 你创建的 ExtendedExecutionSession 种类由你在创建它时提供的 ExtendedExecutionReason 来决定。The kind of ExtendedExecutionSession you create is determined by the ExtendedExecutionReason that you provide when you create it. 有三个 ExtendedExecutionReason 枚举值:Unspecified、LocationTrackingSavingDataThere are three ExtendedExecutionReason enum values: Unspecified, LocationTracking and SavingData. 任何时候都只能请求一个 ExtendedExecutionSession;在已批准的会话请求当前处于活动状态时,尝试创建另一个会话将导致 ExtendedExecutionSession 构造函数引发异常 0x8007139F,指示组或资源不处于执行请求的操作的正确状态。Only one ExtendedExecutionSession can be requested at any time; attempting to create another session while an approved session request is currently active will cause exception 0x8007139F to be thrown from the ExtendedExecutionSession constructor stating that the group or resource is not in the correct state to perform the requested operation. 不要使用 ExtendedExecutionForegroundSessionExtendedExecutionForegroundReason;它们需要受限功能,并且不能在 Microsoft Store 应用程序中使用。Do not use ExtendedExecutionForegroundSession and ExtendedExecutionForegroundReason; they require restricted capabilities and are not available for use in Store applications.

最小化时运行Run while minimized

有两种情况可以使用扩展执行:There are two cases where extended execution can be used:

  • 在常规前台执行期间并且应用程序处于运行状态时。At any point during regular foreground execution, while the application is in the running state.
  • 应用程序的正在挂起事件处理程序收到正在挂起事件之后(操作系统即将把应用转入挂起状态)。After the application has received a suspending event (the OS is about to move the app to the suspended state) in the application’s suspending event handler.

这两种情况下的代码是相同的,但应用程序在不同情况下的行为稍有不同。The code for these two cases is the same, but the application behaves a little differently in each. 在第一种情况下,即使发生通常会触发挂起的事件(例如,用户离开应用程序),应用程序仍保持运行状态。In the first case, the application stays in the running state, even if an event that normally would trigger suspension occurs (for example, the user navigating away from the application). 执行扩展生效期间,应用程序永远不会收到正在挂起事件。The application will never receive a suspending event while the execution extension is in effect. 释放扩展后,应用程序才重新获得挂起资格。When the extension is disposed, the application becomes eligible for suspension again.

在第二种情况下,如果应用程序正在转换到挂起状态,它将在扩展期间保持正在挂起状态。In the second case, if the application is transitioning to the suspended state, it will stay in a suspending state for the period of the extension. 扩展到期后,应用程序进入挂起状态,无需进一步通知。Once the extension expires, the application enters the suspended state without further notification.

针对媒体处理、项目编译或者保持网络连接处于活动状态的情况,创建 ExtendedExecutionSession 以请求应用在进入后台之前运行更长时间时请使用 ExtendedExecutionReason.UnspecifiedUse ExtendedExecutionReason.Unspecified when you create an ExtendedExecutionSession to request additional time before your app moves into the background for scenarios such as media processing, project compilation, or keeping a network connection alive. 在运行适用于桌面版 Windows 10(Home、Pro、Enterprise 和 Education)的桌面设备上,如果需要在最小化应用时避免应用挂起,则可以使用此方法。On desktop devices running Windows 10 for desktop editions (Home, Pro, Enterprise, and Education), this is the approach to use if an app needs to avoid being suspended while it is minimized.

在启动长时间运行的操作时请求扩展,以便延迟挂起状态转换,否则,应用进入后台时就会挂起。Request the extension when starting a long running operation in order to defer the Suspending state transition that otherwise occurs when the app moves into the background. 在桌面设备上,使用 ExtendedExecutionReason.Unspecified 创建的扩展执行会话都有一个电池感知时间限制。On desktop devices, extended execution sessions created with ExtendedExecutionReason.Unspecified have a battery-aware time limit. 如果设备连接到墙上的电源,对扩展执行的时段没有限制。If the device is connected to wall power, there is no limit to the length of the extended execution time period. 如果设备使用电池供电,则扩展执行时段最多可以在后台运行 10 分钟。If the device is on battery power, the extended execution time period can run up to ten minutes in the background.

按应用查看电池使用情况设置中选择允许应用运行后台任务选项时,平板电脑或笔记本用户可以达到同等时长的运行时间,但以牺牲电池使用时间为代价。A tablet or laptop user can get the same long running behavior--at the expense of battery life--when the Allow the app to run background tasks option is selected in Battery usage by app settings. (在便携式计算机上找到此选项,请转到 "设置" " > 系统电池电量按应用" " (" 下的 "设置" "系统 > 电池 > 使用电池电量") > 选择应用 > 关闭Windows > 选择 "允许应用运行后台任务"。(To find this option on a laptop, go to Settings > System > Battery > Battery usage by App (the link under the percent of battery power remaining) > select an app > turn off Managed By Windows > select Allow app to run background tasks.

对于所有操作系统版本,当设备进入“连接待机”模式时,这种扩展执行会话将停止。On all OS editions this kind of extended execution session stops when the device enters Connected Standby. 在运行 Windows 10 移动版的移动设备上,只要屏幕亮着,这种扩展执行会话将一直运行。On mobile devices running Windows 10 Mobile, this kind of extended execution session will run as long as the screen is on. 当屏幕关闭时,设备会立即尝试进入低电量“连接待机”模式。When the screen turns off, the device immediately attempts to enter the low-power Connected-Standby mode. 在桌面设备上,如果出现锁定屏幕,会话将继续运行。On desktop devices, the session will continue running if the lock screen appears. 屏幕关闭后,设备会在一段时间后进入“连接待机”模式。The device does not enter Connected Standby for a period of time after the screen turns off. 在 Xbox OS Edition 上,设备会在 1 小时后进入“连接待机”模式,除非用户更改默认值。On the Xbox OS Edition, the device enters Connect Standby after one hour unless the user changes the default.

跟踪用户位置Track the user's location

如果应用需要定期记录 GeoLocator 中的位置,请在创建 ExtendedExecutionSession 时指定 ExtendedExecutionReason.LocationTrackingSpecify ExtendedExecutionReason.LocationTracking when you create an ExtendedExecutionSession if your app needs to regularly log the location from the GeoLocator. 用于健身跟踪和导航的应用,此应用需要定期监控用户的位置并使用该原因。Apps for fitness tracking and navigation that need to regularly monitor the user's location and should use this reason.

位置跟踪扩展执行会话可以根据需要尽可能长时间运行,在移动设备上锁定屏幕时也是如此。A location tracking extended execution session can run as long as needed, including while the screen is locked on a mobile device. 不过,每个设备上只能运行一个此类会话。However, there can only be one such session running per device. 位置跟踪扩展执行会话只能在前台请求,并且应用必须处于正在运行状态。A location tracking extended execution session can only be requested in the foreground, and the app must be in the Running state. 这确保用户了解应用已启动扩展位置跟踪会话。This ensures that the user is aware that the app has initiated an extended location tracking session. 当应用于后台运行时,仍可以通过后台任务或者应用服务使用 GeoLocator,而无需请求位置跟踪扩展执行会话。It is still possible to use the GeoLocator while the app is in the background by using a background task, or an app service, without requesting a location tracking extended execution session.

本地保存关键数据Save Critical Data Locally

请在创建 ExtendedExecutionSession 时指定 ExtendedExecutionReason.SavingData 来保存用户数据,以免因应用终止前未保存数据而导致数据丢失以及负面用户体验。Specify ExtendedExecutionReason.SavingData when you create an ExtendedExecutionSession to save user data in the case where not saving the data before the app is terminated will result in data loss and a negative user experience.

请勿使用这种会话延长应用生命周期来上载或下载数据。Don't use this kind of session to extend the lifetime of an app to upload or download data. 如果需要上载数据,请在有可用的交流电源时请求 background transfer 或注册 MaintenanceTrigger 来处理传输。If you need to upload data, request a background transfer or register a MaintenanceTrigger to handle the transfer when AC power is available. ExtendedExecutionReason.SavingData 扩展执行会话可以在应用位于前台并且处于正在运行状态时请求,也可以在应用位于后台并且处于挂起状态时请求。A ExtendedExecutionReason.SavingData extended execution session can be requested either when the app is in the foreground and in the Running state, or in the background and in the Suspending state.

应用终止之前,挂起状态是应用在生命周期内可以执行任务的最后机会。The Suspending state is the last opportunity during the app lifecycle that an app can do work before the app is terminated. ExtendedExecutionReason.SavingData 是唯一一种可在挂起状态下请求的 ExtendedExecutionSessionExtendedExecutionReason.SavingData is the only type of ExtendedExecutionSession that can be requested in the Suspending state. 当应用处于挂起状态时请求 ExtendedExecutionReason.SavingData 扩展执行会话会引发潜在问题,你应当引起注意。Requesting a ExtendedExecutionReason.SavingData extended execution session while the app is in the Suspending state creates a potential issue that you should be aware of. 如果在应用处于挂起状态时请求扩展执行会话,并且用户请求再次启动应用,可能要花很长时间启动。If an extended execution session is requested while in the Suspending state, and the user requests the app be launched again, it may appear to take a long time to launch. 这是因为必须完成扩展执行会话时段才能关闭应用的旧实例并启动应用的新实例。This is because the extended execution session time period must complete before the old instance of the app can be closed and a new instance of the app can be launched. 牺牲启动性能时间以保证用户状态不丢失。Launch performance time is sacrificed in order to guarantee that user state is not lost.

请求、处置和吊销Request, disposal, and revocation

与扩展执行会话有 3 个基本的交互:请求、处置和吊销。There are three fundamental interactions with an extended execution session: the request, disposal, and revocation. 以下代码段中对发出请求进行了建模。Making the request is modeled in the following code snippet.

请求Request

var newSession = new ExtendedExecutionSession();
newSession.Reason = ExtendedExecutionReason.Unspecified;
newSession.Revoked += SessionRevoked;
ExtendedExecutionResult result = await newSession.RequestExtensionAsync();

switch (result)
{
    case ExtendedExecutionResult.Allowed:
        DoLongRunningWork();
        break;

    default:
    case ExtendedExecutionResult.Denied:
        DoShortRunningWork();
        break;
}

请参阅代码示例See code sample

调用 RequestExtensionAsync 会向操作系统核实来了解用户是否已批准应用的后台活动以及系统是否有可用资源来启用后台执行。Calling RequestExtensionAsync checks with the operating system to see if the user has approved background activity for the app and whether the system has the available resources to enable background execution. 任何时候系统只为一个应用批准一个会话,对 RequestExtensionAsync 的后续调用将导致会话被拒绝。Only one session will be approved for an app at any time, causing additional calls to RequestExtensionAsync to result in the session being denied.

你可以事先检查 BackgroundExecutionManager 来确定 BackgroundAccessStatus,后者是指示你的应用是否能在后台运行的用户设置。You can check the BackgroundExecutionManager beforehand to determine the BackgroundAccessStatus, which is the user setting that indicates whether your app can run in the background or not. 若要了解这些用户设置的详细信息,请参阅后台活动和能耗感知To learn more about these user settings see Background Activity and Energy Awareness.

ExtendedExecutionReason 指示了应用正在后台执行的操作。The ExtendedExecutionReason indicates the operation your app is performing in the background. Description 字符串是用户可读的字符串,可解释应用需要执行操作的原因。The Description string is a human-readable string that explains why your app needs to perform the operation. 该字符串不呈现给用户,但可能会在日后的 Windows 版本中提供。This string is not presented to the user, but may be made available in a future release of Windows. Revoked 事件处理程序为必需项,以便在用户或系统决定应用不可以再在后台运行时正常终止扩展执行会话。The Revoked event handler is required so that an extended execution session can halt gracefully if the user, or the system, decides that the app can no longer run in the background.

已吊销Revoked

如果应用有处于活动状态的扩展执行会话,并且系统要求后台活动终止(因为前台应用程序需要资源),则吊销会话。If an app has an active extended execution session and the system requires background activity to halt because a foreground application requires the resources, then the session is revoked. 如果事先未引发 Revoked 事件处理程序,则扩展执行会话时段永远不会终止。An extended execution session time period is never terminated without first firing the Revoked event handler.

针对 ExtendedExecutionReason.SavingData 扩展执行会话引发 Revoked 事件时,应用有一秒钟的时间来完成其正在执行的操作并结束挂起When the Revoked event is fired for an ExtendedExecutionReason.SavingData extended execution session, the app has one second to complete the operation it was performing and finish Suspending.

吊销可以出于多种原因:达到执行时间限制、达到后台能耗配额或者需要回收内存以便用户在前台打开新应用。Revocation can occur for many reasons: an execution time limit was reached, a background energy quota was reached, or memory needs to be reclaimed in order for the user to open a new app in the foreground.

以下是 Revoked 事件处理程序示例:Here is an example of a Revoked event handler:

private async void SessionRevoked(object sender, ExtendedExecutionRevokedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        switch (args.Reason)
        {
            case ExtendedExecutionRevokedReason.Resumed:
                rootPage.NotifyUser("Extended execution revoked due to returning to foreground.", NotifyType.StatusMessage);
                break;

            case ExtendedExecutionRevokedReason.SystemPolicy:
                rootPage.NotifyUser("Extended execution revoked due to system policy.", NotifyType.StatusMessage);
                break;
        }

        EndExtendedExecution();
    });
}

请参阅代码示例See code sample

释放Dispose

最后一步是处理扩展执行会话。The final step is to dispose of the extended execution session. 你需要处理会话以及任何其他内存密集型资源,否则,应用在等待会话关闭时所使用的能耗将计入应用的能耗配额。You want to dispose of the session, and any other memory intensive assets, because otherwise the energy used by the app while it is waiting for the session to close will be counted against the app's energy quota. 要为应用保留尽可能多的能耗配额,务必在完成你的会话工作时处理会话,以便应用可以更快速地进入挂起状态。To preserve as much of the energy quota for the app as possible, it is important to dispose of the session when you are done with your work for the session so that the app can move into the Suspended state more quickly.

自行处理会话(而不是等待吊销事件)可降低应用的能耗配额使用情况。Disposing of the session yourself, rather than waiting for the revocation event, reduces your app's energy quota usage. 这意味着在未来的会话中将允许你的应用在后台运行更长时间,因为你有更多的可用能耗配额来执行此操作。This means that your app will be permitted to run in the background longer in future sessions because you'll have more energy quota available to do so. 你必须保留对 ExtendedExecutionSession 对象的引用直到操作结束,这样你便可以调用其 Dispose 方法。You must maintain a reference to the ExtendedExecutionSession object until the end of the operation so that you can call its Dispose method.

处理扩展执行会话的代码段如下所示:A snippet that disposes an extended execution session follows:

void ClearExtendedExecution(ExtendedExecutionSession session)
{
    if (session != null)
    {
        session.Revoked -= SessionRevoked;
        session.Dispose();
        session = null;
    }
}

请参阅代码示例See code sample

一个应用一次只能有一个 ExtendedExecutionSession 处于活动状态。An app can only have one ExtendedExecutionSession active at a time. 很多应用使用异步任务以完成需要访问存储、网络或网络服务等资源的复杂操作。Many apps use asynchronous tasks in order to complete complex operations that require access to resources such as storage, network, or network-based services. 如果需要完成多个异步任务完成操作,则在处理 ExtendedExecutionSession 并允许应用挂起之前必须考虑每个任务的状态。If an operation requires multiple asynchronous tasks to complete, then the state of each of these tasks must be accounted for before disposing the ExtendedExecutionSession and allowing the app to be suspended. 这需要引用计算仍在运行的任务数量,直到值达到零时再处理会话。This requires reference counting the number of tasks that are still running, and not disposing of the session until that value reaches zero.

下面是扩展执行会话期间管理多个任务的一些示例代码。Here is some example code for managing multiple tasks during an extended execution session period. 有关如何在应用中使用该功能的详细信息,请参阅下面链接的代码示例:For more information on how to use this in your app please see the code sample linked below:

static class ExtendedExecutionHelper
{
    private static ExtendedExecutionSession session = null;
    private static int taskCount = 0;

    public static bool IsRunning
    {
        get
        {
            if (session != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public static async Task<ExtendedExecutionResult> RequestSessionAsync(ExtendedExecutionReason reason, TypedEventHandler<object, ExtendedExecutionRevokedEventArgs> revoked, String description)
    {
        // The previous Extended Execution must be closed before a new one can be requested.       
        ClearSession();

        var newSession = new ExtendedExecutionSession();
        newSession.Reason = reason;
        newSession.Description = description;
        newSession.Revoked += SessionRevoked;

        // Add a revoked handler provided by the app in order to clean up an operation that had to be halted prematurely
        if(revoked != null)
        {
            newSession.Revoked += revoked;
        }

        ExtendedExecutionResult result = await newSession.RequestExtensionAsync();

        switch (result)
        {
            case ExtendedExecutionResult.Allowed:
                session = newSession;
                break;
            default:
            case ExtendedExecutionResult.Denied:
                newSession.Dispose();
                break;
        }
        return result;
    }

    public static void ClearSession()
    {
        if (session != null)
        {
            session.Dispose();
            session = null;
        }

        taskCount = 0;
    }

    public static Deferral GetExecutionDeferral()
    {
        if (session == null)
        {
            throw new InvalidOperationException("No extended execution session is active");
        }

        taskCount++;
        return new Deferral(OnTaskCompleted);
    }

    private static void OnTaskCompleted()
    {
        if (taskCount > 0)
        {
            taskCount--;
        }
        
        //If there are no more running tasks than end the extended lifetime by clearing the session
        if (taskCount == 0 && session != null)
        {
            ClearSession();
        }
    }

    private static void SessionRevoked(object sender, ExtendedExecutionRevokedEventArgs args)
    {
        //The session has been prematurely revoked due to system constraints, ensure the session is disposed
        if (session != null)
        {
            session.Dispose();
            session = null;
        }
        
        taskCount = 0;
    }
}

请参阅代码示例See code sample

确保应用正常使用资源Ensure that your app uses resources well

当应用不再是前台应用时,调整应用内存和能耗使用情况是确保操作系统系统允许应用继续运行的关键。Tuning your app's memory and energy use is key to ensuring that the operating system will allow your app to continue to run when it is no longer the foreground app. 使用内存管理 API 查看应用当前使用的内存量。Use the Memory Management APIs to see how much memory your app is using. 当另一个应用在前台运行时,你的应用占用的内存越多,操作系统就越难保持你的应用继续运行。The more memory your app uses, the harder it is for the OS to keep your app running when another app is in the foreground. 用户最终控制你的应用可以执行的所有后台活动,并且可以看到你的应用对电池使用情况的影响。The user is ultimately in control of all background activity that your app can perform and has visibility on the impact your app has on battery use.

使用 BackgroundExecutionManager.RequestAccessAsync 确定用户是否已决定应限制你应用的后台活动。Use BackgroundExecutionManager.RequestAccessAsync to determine if the user has decided that your app’s background activity should be limited. 注意电池使用情况,并且仅当有必要完成用户想要执行的操作时再在后台运行应用。Be aware of your battery usage and only run in the background when it is necessary to complete an action that the user wants.

另请参阅See also

扩展执行示例Extended Execution Sample
应用程序生命周期Application Lifecycle
应用生命周期-通过后台任务和扩展执行使应用保持活动状态 后台内存管理App Lifecycle - Keep Apps Alive with Background Tasks and Extended Execution Background Memory Management
后台传输Background Transfers
电池感知和后台活动Battery Awareness and Background Activity
MemoryManager 类MemoryManager class
在后台播放媒体Play Media in the Background