Udostępnij przez


Tworzenie i rejestrowanie zadania w tle poza procesem

ważne interfejsy API

Utwórz klasę zadań w tle i zarejestruj ją, aby była uruchamiana, gdy aplikacja nie znajduje się na pierwszym planie. W tym temacie pokazano, jak utworzyć i zarejestrować zadanie w tle uruchamiane w osobnym procesie niż proces aplikacji. Aby wykonać zadania w tle bezpośrednio w aplikacji pierwszego planu, zapoznaj się z Tworzenie i rejestrowanie zadania w tle działającego w procesie.

Uwaga / Notatka

Jeśli używasz zadania w tle do odtwarzania multimediów, zobacz Odtwarzanie multimediów w tle, aby uzyskać informacje na temat ulepszeń w systemie Windows 10 w wersji 1607, które znacznie to ułatwiają.

Uwaga / Notatka

Jeśli wdrażasz zadanie działające w tle poza procesem w aplikacji desktopowej języka C# przy użyciu platformy .NET 6 lub nowszej, użyj obsługi tworzenia języka C#/WinRT, aby utworzyć składnik środowiska uruchomieniowego systemu Windows. Dotyczy to aplikacji korzystających z zestawu WINDOWS App SDK, WinUI 3, WPF lub WinForms. Zobacz próbkę zadania w tle jako przykład.

Utwórz klasę Zadania w tle

Kod w tle można uruchomić, pisząc klasy implementujące interfejs IBackgroundTask . Ten kod jest uruchamiany, gdy określone zdarzenie jest wyzwalane przy użyciu na przykład elementu SystemTrigger lub MaintenanceTrigger.

W poniższych krokach pokazano, jak napisać nową klasę, która implementuje interfejs IBackgroundTask .

  1. Utwórz nowy projekt dla zadań w tle i dodaj go do rozwiązania. W tym celu kliknij prawym przyciskiem myszy węzeł rozwiązania w Eksploratorze rozwiązań i wybierz polecenie Dodaj>nowy projekt. Następnie wybierz typ projektu Składnik środowiska uruchomieniowego systemu Windows , nadaj projektowi nazwę i kliknij przycisk OK.
  2. Odwołaj się do projektu zadań w tle w projekcie aplikacji platformy uniwersalnej systemu Windows (UWP). W projekcie aplikacji w języku C# lub C++ kliknij prawym przyciskiem myszy na Referencje i wybierz Dodaj nową referencję. W obszarze Solutionwybierz pozycję Projects, a następnie wybierz nazwę projektu zadania w tle i kliknij przycisk OK.
  3. W projekcie zadań w tle dodaj nową klasę, która implementuje interfejs IBackgroundTask . Metoda IBackgroundTask.Run jest wymaganym punktem wejścia, który będzie wywoływany po wyzwoleniu określonego zdarzenia; ta metoda jest wymagana w każdym zadaniu w tle.

Uwaga / Notatka

Klasy zadań w tle — i wszystkie inne klasy w projekcie zadań w tle — muszą być publicznymi klasami, które są zapieczętowanymi (lub ostatecznymi).

Poniższy przykładowy kod przedstawia bardzo podstawowy punkt wyjścia dla klasy zadań w tle.

// 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. Jeśli uruchomisz dowolny kod asynchroniczny w zadaniu w tle, zadanie w tle musi używać odroczenia. Jeśli nie używasz odroczenia, proces zadania w tle może zostać nieoczekiwanie zakończony, jeśli Metoda Uruchom zostanie zwrócona przed uruchomieniem jakiejkolwiek pracy asynchronicznej.

Zażądaj odroczenia w metodzie Run przed wywołaniem metody asynchronicznej. Zapisz odroczenie do składowej danych klasy, aby można było uzyskać do niej dostęp z metody asynchronicznej. Ogłoś zakończenie odroczenia po zakończeniu kodu asynchronicznego.

Poniższy przykładowy kod pobiera odroczenie, zapisuje go i zwalnia po zakończeniu kodu asynchronicznego.

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

Uwaga / Notatka

W języku C# metody asynchroniczne zadania w tle mogą być wywoływane przy użyciu słów kluczowych async/await . W języku C++/CX można uzyskać podobny wynik przy użyciu łańcucha zadań.

Aby uzyskać więcej informacji na temat wzorców asynchronicznych, zobacz Programowanie asynchroniczne. Aby uzyskać dodatkowe przykłady użycia odroczenia w celu zapobieżenia przedwczesnemu zatrzymaniu zadania w tle, zobacz przykład zadania w tle .

Poniższe kroki są wykonywane w jednej z klas aplikacji (na przykład MainPage.xaml.cs).

Uwaga / Notatka

Możesz również utworzyć funkcję dedykowaną do rejestrowania zadań w tle — zobacz Rejestrowanie zadania w tle. W takim przypadku zamiast używać następnych trzech kroków, możesz po prostu skonstruować wyzwalacz i podać go funkcji rejestracji wraz z nazwą zadania, punktem wejścia zadania i (opcjonalnie) warunkiem.

Zarejestruj zadanie w tle do uruchomienia

  1. Sprawdź, czy zadanie w tle zostało już zarejestrowane przez iterowanie za pomocą właściwości BackgroundTaskRegistration.AllTasks . Ten krok jest ważny; jeśli Twoja aplikacja nie sprawdza istniejących rejestracji zadań w tle, może łatwo zarejestrować zadanie wielokrotnie, powodując problemy z wydajnością i wyczerpując dostępny czas procesora dla zadania, zanim się zakończy.

W poniższym przykładzie iteruje właściwość AllTasks i ustawia zmienną flagową na true, jeśli zadanie zostało już zarejestrowane.

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. Jeśli zadanie w tle nie zostało jeszcze zarejestrowane, użyj narzędzia BackgroundTaskBuilder , aby utworzyć wystąpienie zadania w tle. Punkt wejścia zadania powinien mieć postać nazwy klasy dla zadania w tle, poprzedzonej przestrzenią nazw.

Wyzwalacz zadania w tle kontroluje, kiedy zadanie w tle zostanie uruchomione. Aby uzyskać listę możliwych wyzwalaczy, zobacz SystemTrigger.

Na przykład ten kod tworzy nowe zadanie w tle i ustawia je do uruchomienia po wystąpieniu wyzwalacza 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. Możesz dodać warunek, aby określić, kiedy zadanie zostanie uruchomione po wystąpieniu zdarzenia wyzwalacza (opcjonalnie). Jeśli na przykład nie chcesz, aby zadanie było uruchamiane, dopóki użytkownik nie będzie obecny, użyj warunku UserPresent. Aby uzyskać listę możliwych warunków, zobacz SystemConditionType.

Poniższy przykładowy kod przypisuje warunek wymagający obecności użytkownika:

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. Zarejestruj zadanie w tle, wywołując metodę Register w obiekcie BackgroundTaskBuilder . Zapisz wynik BackgroundTaskRegistration , aby można było go użyć w następnym kroku.

Poniższy kod rejestruje zadanie w tle i przechowuje wynik:

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

Uwaga / Notatka

Aplikacje uniwersalne systemu Windows muszą wywoływać RequestAccessAsync przed zarejestrowaniem dowolnego typu wyzwalacza w tle.

Aby upewnić się, że aplikacja uniwersalna systemu Windows będzie działać prawidłowo po wydaniu aktualizacji, użyj wyzwalacza ServicingComplete (zobacz SystemTriggerType), aby wykonać wszelkie zmiany konfiguracji po aktualizacji, takie jak migrowanie bazy danych aplikacji i rejestrowanie zadań w tle. Najlepszym rozwiązaniem jest wyrejestrowanie zadań w tle skojarzonych z poprzednią wersją aplikacji (zobacz RemoveAccess) i zarejestrowanie zadań w tle dla nowej wersji aplikacji (zobacz RequestAccessAsync) w tej chwili.

Aby uzyskać więcej informacji, zobacz Wytyczne dotyczące zadań w tle.

Obsługa kończenia zadań w tle przy użyciu programów obsługi zdarzeń

Powinieneś zarejestrować metodę w BackgroundTaskCompletedEventHandler, aby aplikacja mogła uzyskać wyniki z zadania działającego w tle. Po uruchomieniu lub wznowieniu aplikacji oznaczona metoda zostanie wywołana, jeśli zadanie w tle zostało ukończone od czasu ostatniego uruchomienia aplikacji na pierwszym planie. (Metoda OnCompleted zostanie wywołana natychmiast, jeśli zadanie w tle zostanie zakończone, gdy aplikacja znajduje się obecnie na pierwszym planie).

  1. Napisz metodę OnCompleted w celu obsługi wykonywania zadań w tle. Na przykład wynik zadania w tle może spowodować aktualizację interfejsu użytkownika. Pokazana tutaj sygnatura metody jest wymagana dla metody obsługi zdarzeń OnCompleted, nawet jeśli w tym przykładzie nie jest używany parametr args.

Poniższy przykładowy kod rozpoznaje uzupełnianie zadań w tle i wywołuje przykładową metodę aktualizacji interfejsu użytkownika, która pobiera ciąg komunikatu.

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

Uwaga / Notatka

Aktualizacje interfejsu użytkownika powinny być wykonywane asynchronicznie, aby uniknąć wstrzymywania wątku interfejsu użytkownika. Na przykładzie można zobaczyć metodę UpdateUI w przykładowym zadaniu w tle .

  1. Wróć do miejsca, w którym zarejestrowano zadanie w tle. Po tym wierszu kodu dodaj nowy obiekt BackgroundTaskCompletedEventHandler . Podaj metodę OnCompleted jako parametr konstruktora BackgroundTaskCompletedEventHandler .

Poniższy przykładowy kod dodaje BackgroundTaskCompletedEventHandler do BackgroundTaskRegistration:

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

Zadeklaruj w manifeście aplikacji, że aplikacja używa zadań w tle

Aby aplikacja mogła uruchamiać zadania w tle, należy zadeklarować każde zadanie w tle w manifeście aplikacji. Jeśli aplikacja spróbuje zarejestrować zadanie w tle za pomocą wyzwalacza, który nie znajduje się na liście w manifeście, rejestracja zadania w tle zakończy się niepowodzeniem z powodu błędu "klasa czasu wykonania niezarejestrowana".

  1. Otwórz projektanta manifestu pakietu, otwierając plik o nazwie Package.appxmanifest.
  2. Otwórz kartę o nazwie Deklaracje .
  3. Z listy rozwijanej dostępnych deklaracji wybierz pozycję zadania w tle i kliknij pozycję Dodaj.
  4. Zaznacz pole wyboru zdarzenia systemowego.
  5. W polu tekstowym Entry point: wprowadź przestrzeń nazw i nazwę klasy w tle, która w tym przykładzie to Tasks.ExampleBackgroundTask.
  6. Zamknij projektanta manifestu.

Następujący element Extensions jest dodawany do pliku Package.appxmanifest w celu zarejestrowania zadania w tle:

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

Podsumowanie i następne kroki

Teraz należy zrozumieć podstawy pisania klasy zadań w tle, sposobu rejestrowania zadania w tle z poziomu aplikacji oraz sposobu rozpoznawania aplikacji po zakończeniu zadania w tle. Należy również zrozumieć, jak zaktualizować manifest aplikacji, aby aplikacja mogła pomyślnie zarejestrować zadanie w tle.

Uwaga / Notatka

Pobierz przykład zadania w tle , aby wyświetlić podobne przykłady kodu w kontekście kompletnej i niezawodnej aplikacji UWP korzystającej z zadań w tle.

Zapoznaj się z następującymi tematami pokrewnymi, aby zapoznać się z dokumentacją interfejsu API, wskazówkami koncepcyjnymi dotyczącymi zadań w tle i bardziej szczegółowymi instrukcjami dotyczącymi pisania aplikacji korzystających z zadań w tle.

Szczegółowe instrukcje dotyczące zadań w tle

Wskazówki dotyczące zadań w tle

Dokumentacja referencyjna interfejsu API zadań w tle