建立及註冊跨處理序的背景工作

重要 API

建立背景工作類別,並在您的應用程式不在前景時註冊以執行。 本主題示範如何建立和註冊在與應用程式程序不同的程序中執行的背景工作。 若要直接在前景應用程式中執行背景工作,請參閱建立及註冊程序內背景工作

注意

如果您使用背景工作在背景播放媒體,請參閱在背景播放媒體,以取得 Windows 10 版本 1607 改善的相關資訊,讓媒體更容易。

注意

如果您要使用 .NET 6 或更新版本在 C# 傳統型應用程式中實作跨程序背景工作,請使用 C#/WinRT 撰寫支援來建立 Windows 執行階段元件。 這適用於使用 Windows 應用程式 SDK、WinUI 3、WPF 或 WinForms 的應用程式。 如需範例,請參閱背景工作範例

建立背景工作類別

您可以撰寫實作 IBackgroundTask 介面的類別,在背景執行程序代碼。 當使用 SystemTriggerMaintenanceTrigger 等觸發特定事件時,此程式碼將運行。

下列步驟示範如何撰寫實作 IBackgroundTask 介面的新類別。

  1. 為背景任務建立一個新專案,並將其添加到解決方案中。 若要這樣做,請在方案總管中選取新增>新專案。 然後選取 Windows 執行階段元件專案類型、命名專案,然後按兩下確定。
  2. 從通用 Windows 平台 (UWP) 應用程式項目參考背景工作專案。 對於 C# 或 C++ 應用程式,在應用程式專案中,右鍵點擊引用並選擇新增引用。 在解決方案下,選擇項目,然後選擇背景任務專案的名稱並按一下確定
  3. 在背景工作專案中,新增實作 IBackgroundTask 介面的新類別。 IBackgroundTask.Run 方法是觸發指定事件時呼叫的必要進入點;每個背景工作都需要此方法。

注意

背景任務類別本身以及背景任務項目中的所有其他類別都需要是密封的 (或最終) 公共類別。

下列範例程式代碼顯示背景工作類別的基本起點。

// ExampleBackgroundTask.cs
using Windows.ApplicationModel.Background;

namespace Tasks
{
    public sealed class ExampleBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            
        }        
    }
}
// First, add ExampleBackgroundTask.idl, and then build.
// ExampleBackgroundTask.idl
namespace Tasks
{
    [default_interface]
    runtimeclass ExampleBackgroundTask : Windows.ApplicationModel.Background.IBackgroundTask
    {
        ExampleBackgroundTask();
    }
}

// ExampleBackgroundTask.h
#pragma once

#include "ExampleBackgroundTask.g.h"

namespace winrt::Tasks::implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask>
    {
        ExampleBackgroundTask() = default;

        void Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance);
    };
}

namespace winrt::Tasks::factory_implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask, implementation::ExampleBackgroundTask>
    {
    };
}

// ExampleBackgroundTask.cpp
#include "pch.h"
#include "ExampleBackgroundTask.h"

namespace winrt::Tasks::implementation
{
    void ExampleBackgroundTask::Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
    {
        throw hresult_not_implemented();
    }
}
// ExampleBackgroundTask.h
#pragma once

using namespace Windows::ApplicationModel::Background;

namespace Tasks
{
    public ref class ExampleBackgroundTask sealed : public IBackgroundTask
    {

    public:
        ExampleBackgroundTask();

        virtual void Run(IBackgroundTaskInstance^ taskInstance);
        void OnCompleted(
            BackgroundTaskRegistration^ task,
            BackgroundTaskCompletedEventArgs^ args
        );
    };
}

// ExampleBackgroundTask.cpp
#include "ExampleBackgroundTask.h"

using namespace Tasks;

void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
}
  1. 如果您在背景工作中執行任何非同步程序代碼,則背景工作必須使用延遲。 如果您未使用延遲,則如果 Run 方法在任何非同步工作執行完成之前傳回,背景工作程序可能會意外終止。

在呼叫非同步方法之前,請先要求 Run 方法中的延遲。 將延遲儲存至類別資料成員,以便從非同步方法存取它。 在非同步程式代碼完成之後宣告延遲完成。

下列範例程式代碼會取得延遲、儲存,並在非同步程式代碼完成時釋放它。

BackgroundTaskDeferral _deferral; // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation
public async void Run(IBackgroundTaskInstance taskInstance)
{
    _deferral = taskInstance.GetDeferral();
    //
    // TODO: Insert code to start one or more asynchronous methods using the
    //       await keyword, for example:
    //
    // await ExampleMethodAsync();
    //

    _deferral.Complete();
}
// ExampleBackgroundTask.h
...
private:
    Windows::ApplicationModel::Background::BackgroundTaskDeferral m_deferral{ nullptr };

// ExampleBackgroundTask.cpp
...
Windows::Foundation::IAsyncAction ExampleBackgroundTask::Run(
    Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
{
    m_deferral = taskInstance.GetDeferral(); // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation.
    // TODO: Modify the following line of code to call a real async function.
    co_await ExampleCoroutineAsync(); // Run returns at this point, and resumes when ExampleCoroutineAsync completes.
    m_deferral.Complete();
}
void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
    m_deferral = taskInstance->GetDeferral(); // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation.

    //
    // TODO: Modify the following line of code to call a real async function.
    //       Note that the task<void> return type applies only to async
    //       actions. If you need to call an async operation instead, replace
    //       task<void> with the correct return type.
    //
    task<void> myTask(ExampleFunctionAsync());

    myTask.then([=]() {
        m_deferral->Complete();
    });
}

注意

在 C# 中,您可以使用 async/await 關鍵詞呼叫背景工作的非同步方法。 在 C++/CX 中,您可以使用工作鏈結來達成類似的結果。

有關非同步模式的更多資訊,請參閱非同步程式設計。 如需如何使用延遲來防止背景工作提早停止的其他範例,請參閱背景工作範例

下列步驟會在其中一個應用程式類別中完成 (例如,MainPage.xaml.cs)。

注意

您也可以建立專用於註冊背景工作的函式,請參閱 註冊背景工作。 在此情況下,您可以直接建構觸發程式,並將它連同工作名稱、工作進入點和 (選擇性) 條件一起提供給註冊函式,而不是使用後續三個步驟。

註冊背景任務執行

  1. 查看 BackgroundTaskRegistration.AllTasks 屬性是否已註冊背景工作。 這一步很重要;如果您的應用程式沒有檢查現有的背景任務註冊,它很容易多次註冊該任務,從而導致效能問題,並在工作完成之前耗盡任務的可用 CPU 時間。

下列範例會反覆運算 AllTasks 屬性,並在工作已註冊時將旗標變數設定為 true。

var taskRegistered = false;
var exampleTaskName = "ExampleBackgroundTask";

foreach (var task in BackgroundTaskRegistration.AllTasks)
{
    if (task.Value.Name == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }
}
std::wstring exampleTaskName{ L"ExampleBackgroundTask" };

auto allTasks{ Windows::ApplicationModel::Background::BackgroundTaskRegistration::AllTasks() };

bool taskRegistered{ false };
for (auto const& task : allTasks)
{
    if (task.Value().Name() == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }
}

// The code in the next step goes here.
boolean taskRegistered = false;
Platform::String^ exampleTaskName = "ExampleBackgroundTask";

auto iter = BackgroundTaskRegistration::AllTasks->First();
auto hascur = iter->HasCurrent;

while (hascur)
{
    auto cur = iter->Current->Value;

    if(cur->Name == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }

    hascur = iter->MoveNext();
}
  1. 如果背景工作尚未註冊,請使用 BackgroundTaskBuilder 來建立背景工作的執行個體。 工作進入點應該是背景工作類別的名稱,前面加上命名空間。

背景任務觸發器控制背景任務何時運行。 如需可能的觸發程式清單,請參閱 SystemTrigger

例如,此程式代碼會建立新的背景工作,並將它設定為在 TimeZoneChanged 觸發程式發生時執行:

var builder = new BackgroundTaskBuilder();

builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.ExampleBackgroundTask";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
if (!taskRegistered)
{
    Windows::ApplicationModel::Background::BackgroundTaskBuilder builder;
    builder.Name(exampleTaskName);
    builder.TaskEntryPoint(L"Tasks.ExampleBackgroundTask");
    builder.SetTrigger(Windows::ApplicationModel::Background::SystemTrigger{
        Windows::ApplicationModel::Background::SystemTriggerType::TimeZoneChange, false });
    // The code in the next step goes here.
}
auto builder = ref new BackgroundTaskBuilder();

builder->Name = exampleTaskName;
builder->TaskEntryPoint = "Tasks.ExampleBackgroundTask";
builder->SetTrigger(ref new SystemTrigger(SystemTriggerType::TimeZoneChange, false));
  1. 您可以新增條件來控制工作在觸發程式事件發生後執行的時間 (選擇性)。 例如,如果您不希望任務在使用者存在之前執行,請使用條件 UserPresent。 如需可能條件的清單,請參閱 SystemConditionType

下列範例程式代碼會指派條件,要求使用者存在:

builder.AddCondition(new SystemCondition(SystemConditionType.UserPresent));
builder.AddCondition(Windows::ApplicationModel::Background::SystemCondition{ Windows::ApplicationModel::Background::SystemConditionType::UserPresent });
// The code in the next step goes here.
builder->AddCondition(ref new SystemCondition(SystemConditionType::UserPresent));
  1. BackgroundTaskBuilder 物件上呼叫 Register 方法,以註冊背景工作。 儲存 BackgroundTaskRegistration 結果,以便在下一個步驟中使用。

下列程式代碼會註冊背景工作並儲存結果:

BackgroundTaskRegistration task = builder.Register();
Windows::ApplicationModel::Background::BackgroundTaskRegistration task{ builder.Register() };
BackgroundTaskRegistration^ task = builder->Register();

注意

通用 Windows 平台應用程式註冊任何背景觸發程序類型之前,必須呼叫 RequestAccessAsync

為了確保您的通用 Windows 應用程式在您發行更新之後繼續正常執行,請使用 ServicingComplete (請參閱 SystemTriggerType) 觸發程式來執行任何更新後設定變更,例如移轉應用程式的資料庫和註冊背景工作。 最佳做法是取消註冊與應用程式的先前版本關聯的背景任務 (請參閱RemoveAccess),並此時為新版本的應用程式註冊背景任務 (請參閱 RequestAccessAsync)。

如需詳細資訊,請參閱背景任務指南

使用事件處理程序處理背景工作完成

您應該向 BackgroundTaskCompletedEventHandler 註冊方法,讓您的應用程式可以從背景工作取得結果。 當應用程式啟動或復原時,如果自上次應用程式位於前台以來背景任務已完成,則會呼叫標記的方法。 (如果您的應用程式目前在前景時背景工作完成,將會立即呼叫 OnCompleted 方法。)

  1. 撰寫 OnCompleted 方法來處理背景工作的完成。 例如,背景工作結果可能會導致 UI 更新。 雖然此範例不使用 args 參數,但 OnCompleted 事件處理程式方法仍需要此處顯示的方法使用量。

以下範例程式碼識別背景任務完成並呼叫採用訊息字串的範例 UI 更新方法。

private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
{
    var settings = Windows.Storage.ApplicationData.Current.LocalSettings;
    var key = task.TaskId.ToString();
    var message = settings.Values[key].ToString();
    UpdateUI(message);
}
void UpdateUI(winrt::hstring const& message)
{
    MyTextBlock().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [=]()
    {
        MyTextBlock().Text(message);
    });
}

void OnCompleted(
    Windows::ApplicationModel::Background::BackgroundTaskRegistration const& sender,
    Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs const& /* args */)
{
	// You'll previously have inserted this key into local settings.
    auto settings{ Windows::Storage::ApplicationData::Current().LocalSettings().Values() };
    auto key{ winrt::to_hstring(sender.TaskId()) };
    auto message{ winrt::unbox_value<winrt::hstring>(settings.Lookup(key)) };

    UpdateUI(message);
}
void MainPage::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
{
    auto settings = ApplicationData::Current->LocalSettings->Values;
    auto key = task->TaskId.ToString();
    auto message = dynamic_cast<String^>(settings->Lookup(key));
    UpdateUI(message);
}

注意

UI 更新應該以非同步方式執行,以避免按住 UI 線程。 有關範例,請參閱背景任務範例中的 UpdateUI 方法。

  1. 返回您註冊背景工作的位置。 在該程式代碼行之後,新增 BackgroundTaskCompletedEventHandler 物件。 提供 OnCompleted 方法做為 BackgroundTaskCompletedEventHandler 建構函式的參數。

以下範例程式碼將 BackgroundTaskCompletedEventHandler 新增至 BackgroundTaskRegistration

task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
task.Completed({ this, &MainPage::OnCompleted });
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &MainPage::OnCompleted);

在應用程式指令清單中宣告您的應用程式使用背景工作

您必須先在應用程式指令清單中宣告每個背景工作,您的應用程式才能執行背景工作。 如果您的應用程式嘗試向指令清單中未列出的觸發程式註冊背景工作,背景工作的註冊將會失敗,並出現「運行時間類別未註冊」錯誤。

  1. 透過開啟名為 Package.appxmanifest 的檔案來開啟套件清單設計器。
  2. 打開聲明索引標籤。
  3. 可用聲明下拉清單中,選擇背景任務並按一下新增
  4. 選取系統事件核取方塊。
  5. 進入點:文字方塊中,輸入背景類別的命名空間和名稱,也就是 Tasks.ExampleBackgroundTask。
  6. 關閉最精明的設計工具。

下列 Extensions 元素會新增至 Package.appxmanifest 檔案,以註冊背景工作:

<Extensions>
  <Extension Category="windows.backgroundTasks" EntryPoint="Tasks.ExampleBackgroundTask">
    <BackgroundTasks>
      <Task Type="systemEvent" />
    </BackgroundTasks>
  </Extension>
</Extensions>

摘要和後續步驟

現在您應該了解如何編寫背景任務類別、如何從應用程式內註冊背景任務以及如何讓應用程式識別背景任務何時完成的基礎知識。 您還應該了解如何更新應用程式清單,以便您的應用程式可以成功註冊背景任務。

注意

下載背景工作範例,以查看使用背景工作的完整健全 UWP app 內容中的類似程式碼範例。

如需 API 參考、背景工作概念指引,以及撰寫使用背景工作之應用程式的詳細指示,請參閱下列相關主題。

詳細的背景工作指示性主題

背景工作指引

背景工作 API 參考