共用方式為


透過延長執行延後應用程式暫停

本文說明如何在應用程式暫停時使用延長執行來延後,使其可在最小化或鎖定畫面下執行。

當使用者將應用程式最小化或切換離開時,會進入暫停狀態。 其記憶體會維持,但其程式碼不會執行。 在所有具有視覺化使用者介面的 OS 版本上都是如此。 如需應用程式暫停時的詳細資訊,請參閱應用程式生命週期

在某些情況下,當使用者離開應用程式,或最小化應用程式時,可能需要繼續執行,而不是暫停。 例如,即使使用者切換到其他應用程式,計步應用程式需要持續執行和追蹤步數。

如果應用程式需要繼續執行,作業系統可以讓程式保持執行,程式也可以要求繼續執行。 例如,在背景播放音訊時,如果您遵循背景媒體播放的這些步驟,OS 可以讓應用程式保持執行的時間更長。 否則,您必須手動要求更多時間。 您可以執行背景執行的時間量可能是幾分鐘,但您必須準備好隨時處理工作階段被撤銷的情況。 當應用程式在偵錯工具下執行時,會停用這些應用程式生命週期時間限制。 基於這個理由,請務必測試延長執行和其他工具,以在偵錯工具下執行時延後應用程式暫停,或使用 Visual Studio 中可用的生命週期事件。

建立 ExtendedExecutionSession 以要求更多時間在背景中完成作業。 您所建立的 ExtendedExecutionSession 類型是由您在建立時提供的 ExtendedExecutionReason 決定。 ExtendedExecutionReason 有三個列舉值:Unspecified、LocationTrackingSavingData。 任何時間只能要求一個 ExtendedExecutionSession;在已經核准的工作階段要求正在進行時,嘗試建立另一個工作階段將導致從 ExtendedExecutionSession 建構函式擲回 0x8007139F 例外狀況,指出該群組或資源處於執行所要求操作的正確狀態之外。 請勿使用 ExtendedExecutionForegroundSessionExtendedExecutionForegroundReason;它們需要受限制的功能,而且無法在 Store 應用程式中使用。

最小化時執行

有兩種情況可以使用延長執行:

  • 在一般前景執行期間的任何時間點,當應用程式處於執行中狀態時。
  • 在應用程式收到暫停事件之後,應用程式會在應用程式的暫停事件處理常式中,將應用程式移至暫停狀態。

這兩個案例的程式碼相同,但應用程式在每個案例中的行為稍有不同。 在第一個案例中,應用程式會保持執行中狀態,即使通常會觸發暫停的事件也一樣 (例如,使用者離開應用程式)。 執行延長生效時,應用程式永遠不會收到暫停事件。 當延長受到處置時,應用程式會再次具備暫停的資格。

在第二個案例中,如果應用程式轉換至暫停狀態,則會在延長期間維持暫停狀態。 延長時間到期後,應用程式會進入暫停狀態,而不需進一步通知。

當您建立 ExtendedExecutionSession 時,請使用 ExtendedExecutionReason.Unspecified,以在應用程式移至背景之前要求額外的時間,例如媒體處理、專案編譯,或讓網路連線保持連線。 在執行 Windows 10 桌面版 (家用版、專業版、企業版和教育版) 的桌上型裝置上,如果應用程式需要避免在最小化時暫停,就是使用這個方法。

啟動長時間執行的作業時,要求延長以延遲暫停狀態轉換,否則應用程式移至背景時會發生的狀態轉換。 在桌面裝置上,使用 ExtendedExecutionReason.Unspecified 所建立的延長執行工作階段,具有依賴電池的時間限制。 如果裝置連接到牆上電源,則延長執行期間的時間長度不受限制。 若裝置使用電池供電,則延長執行期間可在背景中執行長達十分鐘。

平板電腦或筆記型電腦使用者可以在電池使用時間的代價下獲得相同的長時間執行行為,當在 [應用程式的電池使用情況] 設定中選擇了 [允許應用程式執行背景工作] 選項時。 (若要在筆記型電腦上尋找此選項,請前往 [設定>系統>電池>應用程式的電池使用量] (剩餘電池電量百分比下方的連結) > 選取應用程式 > 關閉 [由 Windows 管理>] 選取 [允許應用程式執行背景工作]。

在所有的作業系統版本中,這種延長執行工作階段會在裝置備進入連線待機模式時停止。 在執行 Windows 10 Mobile 的行動裝置上,只要螢幕是開啟的,這種延長執行工作階段就會執行。 當螢幕關閉時,裝置立即嘗試進入低電源的連線待機模式。 在桌上型裝置上,如果出現鎖定畫面,工作階段將會繼續執行。 螢幕關閉之後,裝置在一段時間內不會進入連線待機模式。 在 Xbox 作業系統版本上,除非使用者更改預設設定,否則裝置會在一小時後進入連線待機模式。

追蹤使用者的位置

當您建立 ExtendedExecutionSession 時,如果您的應用程式需要定期記錄 GeoLocator 的位置,請指定 ExtendedExecutionReason.LocationTracking。 需要定期監控使用者位置的健身追蹤與導航應用程式應該使用此功能。

位置追蹤延長執行工作階段可以持續執行,包括在行動裝置的螢幕被鎖定時。 不過,每個裝置只能執行一個這類工作階段。 位置追蹤延長執行工作階段只能在前景中要求,並且應用程式必須處於執行中狀態。 這確保使用者知道應用程式已啟動了延長的位置追蹤工作階段。 當應用程式處於背景執行時,仍然可以使用 GeoLocator,方法是使用背景工作或應用程式服務,而不需要求位置追蹤延長執行工作階段。

在本機儲存重要資料

當您建立 ExtendedExecutionSession 以儲存使用者資料時,請指定 ExtendedExecutionReason.SavingData,以在應用程式終止前未儲存資料時,會導致資料遺失和不佳的使用者體驗。

請勿使用這種工作階段來延長應用程式的存留期,以上傳或下載資料。 如果您需要上傳資料,請要求背景傳輸或註冊MaintenanceTrigger ,以在 AC 電源可用時處理傳輸。 當應用程式處於前景和執行中狀態,或在背景和暫停狀態時,可以要求 ExtendedExecutionReason.SavingData 擴充執行工作階段。

暫停狀態是應用程式生命週期期間,應用程式可以在應用程式終止之前執行工作的最後機會。 ExtendedExecutionReason.SavingData 是唯一可在暫停狀態要求的 ExtendedExecutionSession 類型。 當應用程式處於暫停狀態時,要求 ExtendedExecutionReason.SavingData 擴充執行會話,會建立您應該注意的潛在問題。 如果在暫停狀態時要求延長執行工作階段,且使用者要求再次啟動應用程式,則可能需要很長的時間才能啟動。 這是因為延伸執行工作階段期間必須先完成,才能關閉應用程式的舊執行個體,而且可以啟動應用程式的新執行個體。 為了確保使用者狀態不遺失,因此犧牲了啟動效能時間。

要求、處置和撤銷

在延長執行階段中有三種基本的互動:要求、處置和撤銷。 提出要求會在下列程式碼片段中建立模型。

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;
}

請參閱程式碼範例

呼叫 RequestExtensionAsync 會檢查作業系統,以查看使用者是否已核准應用程式的背景活動,以及系統是否有可用資源來啟用背景執行。 一次只能為應用程式核准一個工作階段,導致對 RequestExtensionAsync 的額外呼叫導致工作階段被拒絕。

您可以事先檢查 BackgroundExecutionManager 來判斷 BackgroundAccessStatus,這是指出您的應用程式是否可以在背景中執行的使用者設定。 若要深入瞭解這些使用者設定,請參閱背景活動和能源意識

ExtendedExecutionReason 指出您的應用程式在背景執行的作業。 Description 字串是人類可讀取的字串,可解釋應用程式執行作業的原因。 此字串不會向使用者顯示,但可能會在未來的 Windows 版本中提供。 需要 Revoked 事件處理常式,以便在使用者或系統決定應用程式無法再於背景中執行時,可以正常停止延長執行工作階段。

撤銷

若應用程式具有作用中的延長執行階段,且系統需要停止背景活動以提供前景應用程式所需的資源,則會撤銷該執行階段。 若未先引發 Revoked 事件處理常式,就不會終止延長的執行工作階段期間。

當針對 ExtendedExecutionReason.SavingData 延長執行工作階段引發 Revoked 事件時,應用程式有一秒可完成正在執行的作業並完成暫停

可能出現撤銷的原因有很多:達到執行時間限制、達到背景能源配額,或者需要回收記憶體,以便使用者在前景中開啟新應用程式。

以下是 Revoked 事件處理常式的範例:

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();
    });
}

請參閱程式碼範例

處置

最後一個步驟是處置延長執行工作階段。 您想處置這個工作階段以及其他佔用大量記憶體的資源,否則在等待工作階段關閉時應用程式消耗的能源將會計入應用程式的能源配額。 若要盡可能保留應用程式的能源配額,當您完成工作階段的工作時,請務必處置工作階段,讓應用程式可以更快速地移至暫停狀態。

自行處置工作階段,而不是等待撤銷事件,可減少您應用程式的能源配額使用量。 這表示您的應用程式在未來工作階段中將允許在背景中執行更長的時間,因為您將有更多的能源配額可供執行。 您必須維護 ExtendedExecutionSession 物件的參考,直到作業結束,才能呼叫它的 Dispose 方法。

處置延長執行工作階段的程式碼片段如下:

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

請參閱程式碼範例

應用程式一次只能有一個作用中的 ExtendedExecutionSession。 許多應用程式會使用非同步工作來完成需要存取儲存空間、網路或網路型服務等資源的複雜作業。 如果作業需要多個非同步工作才能完成,則在處置 ExtendedExecutionSession 並允許暫停應用程式之前,必須考慮每個工作的狀態。 這需要對仍在執行中的工作數目進行參考計數,而且要等到該值達到零,才會處置工作階段。

以下是在延長執行工作階段期間管理多個工作的一些範例程式碼。 如需如何在應用程式中使用此項目的詳細資訊,請參閱下列連結的程式碼範例:

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;
    }
}

請參閱程式碼範例

確保應用程式有效利用資源

調整應用程式的記憶體和能源使用,是確保作業系統在應用程式不再是前景應用程式時仍能繼續執行的關鍵。 使用記憶體管理 API 來查看您的應用程式所使用的記憶體量。 應用程式所使用的記憶體越多,當另一個應用程式處於前景時,作業系統就越難讓您的應用程式繼續執行。 使用者最終會控制應用程式可執行的所有背景活動,並能看見應用程式對電池使用電量的影響。

使用 BackgroundExecutionManager.RequestAccessAsync 來判斷使用者是否已決定您的應用程式背景活動應受到限制。 請注意您的電池使用量,且只有在需要完成使用者想要的動作時,才會在背景中執行。

另請參閱

延長執行範例
應用程式週期
應用程式生命週期 - 使用背景工作和延長執行來保持應用程式執行背景記憶體管理
背景傳輸
電池意識和背景活動
MemoryManager 類別
在背景播放媒體