Создание и регистрация внепроцессной фоновой задачи
Важные API
Создайте класс фоновой задачи и зарегистрируйте его для запуска, если приложение не находится на переднем плане. В этом разделе показано, как создать и зарегистрировать фоновую задачу, которая выполняется в отдельном процессе, чем процесс приложения. Чтобы выполнить фоновую работу непосредственно в приложении переднего плана, см. статью "Создание и регистрация фоновой задачи в процессе".
Примечание.
Если вы используете фоновую задачу для воспроизведения мультимедиа в фоновом режиме, ознакомьтесь со сведениями об улучшениях в Windows 10 версии 1607, что значительно упрощает воспроизведение мультимедиа.
Примечание.
Если вы реализуете внепроцессную фоновую задачу в классическом приложении C# с .NET 6 или более поздней версии, используйте поддержку разработки C#/WinRT для создания компонента среда выполнения Windows. Это относится к приложениям с помощью пакета SDK для приложений Windows, WinUI 3, WPF или WinForms. Пример фоновой задачи см. в примере.
Создание класса фоновой задачи
Вы можете запустить код в фоновом режиме, написав классы, реализующие интерфейс IBackgroundTask . Этот код запускается при активации определенного события, например SystemTrigger или MaintenanceTrigger.
Ниже показано, как написать новый класс, реализующий интерфейс IBackgroundTask .
- Создайте проект для фоновых задач и добавьте его в решение. Для этого щелкните правой кнопкой мыши узел решения в Обозреватель решений и выберите "Добавить>новый проект". Затем выберите тип проекта компонента среда выполнения Windows, присвойте проекту имя и нажмите кнопку "ОК".
- Ссылка на проект фоновых задач из проекта приложения универсальная платформа Windows (UWP). Для приложения C# или C++ в проекте приложения щелкните правой кнопкой мыши ссылки и выберите "Добавить новую ссылку". В разделе "Решение" выберите "Проекты" , а затем выберите имя проекта фоновой задачи и нажмите кнопку "ОК".
- В проект фоновых задач добавьте новый класс, реализующий интерфейс 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)
{
}
- Если вы запускаете любой асинхронный код в фоновой задаче, фоновая задача должна использовать отсрочку. Если вы не используете отсрочку, фоновый процесс задачи может неожиданно завершиться, если метод 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).
Примечание.
Вы также можете создать функцию, выделенную для регистрации фоновых задач, см. раздел "Регистрация фоновой задачи". В этом случае вместо следующих трех шагов можно просто создать триггер и предоставить его функции регистрации вместе с именем задачи, точкой входа задачи и (необязательно) условием.
Регистрация фоновой задачи для выполнения
- Узнайте, зарегистрирована ли фоновая задача, выполнив итерацию по свойству BackgroundTaskRegistration.AllTasks . Этот шаг важен; Если приложение не проверяет наличие регистрации фоновых задач, это может легко зарегистрировать задачу несколько раз, что приводит к проблемам с производительностью и максимальной производительностью задачи до завершения работы.
В следующем примере выполняется итерацию свойства 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();
}
- Если фоновая задача еще не зарегистрирована, используйте 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));
- Вы можете добавить условие для управления выполнением задачи после возникновения события триггера (необязательно). Например, если вы не хотите, чтобы задача выполнялось до тех пор, пока пользователь не будет присутствовать, используйте условие 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));
- Зарегистрируйте фоновую задачу, вызвав метод Register в объекте BackgroundTaskBuilder . Сохраните результат BackgroundTaskRegistration , чтобы его можно было использовать на следующем шаге.
Следующий код регистрирует фоновую задачу и сохраняет результат:
BackgroundTaskRegistration task = builder.Register();
Windows::ApplicationModel::Background::BackgroundTaskRegistration task{ builder.Register() };
BackgroundTaskRegistration^ task = builder->Register();
Примечание.
Универсальные приложения Windows должны вызывать RequestAccessAsync перед регистрацией любого из типов фоновых триггеров.
Чтобы обеспечить правильную работу приложения универсального windows после выпуска обновления, используйте триггер ServiceingComplete (см . SystemTriggerType) для выполнения любых изменений конфигурации после обновления, таких как перенос базы данных приложения и регистрация фоновых задач. Рекомендуется отменить регистрацию фоновых задач, связанных с предыдущей версией приложения (см. раздел RemoveAccess) и зарегистрировать фоновые задачи для новой версии приложения (см. раздел RequestAccessAsync) в настоящее время.
Дополнительные сведения см. в руководстве по фоновым задачам.
Обработка завершения фоновой задачи с помощью обработчиков событий
Необходимо зарегистрировать метод в BackgroundTaskCompletedEventHandler, чтобы приложение получите результаты из фоновой задачи. Когда приложение запускается или возобновляется, помеченный метод будет вызываться, если фоновая задача завершена с момента последнего запуска приложения на переднем плане. (Метод OnCompleted будет вызываться немедленно, если фоновая задача завершается, пока приложение находится на переднем плане.)
- Напишите метод OnCompleted для обработки завершения фоновых задач. Например, результат фоновой задачи может привести к обновлению пользовательского интерфейса. Показанный здесь объем метода необходим для метода обработчика событий OnCompleted, несмотря на то, что в этом примере параметр args не используется.
В следующем примере кода распознается завершение фоновой задачи и вызывается пример метода обновления пользовательского интерфейса, который принимает строку сообщения.
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);
}
Примечание.
Обновления пользовательского интерфейса должны выполняться асинхронно, чтобы избежать удержания потока пользовательского интерфейса. Пример см. в методе UpdateUI в примере фоновой задачи.
- Вернитесь к месту регистрации фоновой задачи. После этой строки кода добавьте новый объект BackgroundTaskCompletedEventHandler. Укажите метод OnCompleted в качестве параметра конструктора BackgroundTaskCompletedEventHandler .
Следующий пример кода добавляет BackgroundTaskCompletedEventHandler в BackgroundTaskRegistration:
task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
task.Completed({ this, &MainPage::OnCompleted });
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &MainPage::OnCompleted);
Объявление в манифесте приложения о том, что приложение использует фоновые задачи
Прежде чем приложение сможет выполнять фоновые задачи, необходимо объявить каждую фоновую задачу в манифесте приложения. Если приложение пытается зарегистрировать фоновую задачу с триггером, который не указан в манифесте, регистрация фоновой задачи завершится ошибкой "класс среды выполнения не зарегистрирован".
- Откройте конструктор манифеста пакета, открыв файл с именем Package.appxmanifest.
- Откройте вкладку "Объявления".
- В раскрывающемся списке "Доступные объявления" выберите фоновые задачи и нажмите кнопку "Добавить".
- Установите флажок " Системное событие ".
- В поле "Точка входа": текстовое поле введите пространство имен и имя фонового класса, который является примером Tasks.ExampleBackgroundTask.
- Закройте конструктор манифестов.
Следующий элемент Extensions добавляется в файл Package.appxmanifest для регистрации фоновой задачи:
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="Tasks.ExampleBackgroundTask">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
</Extensions>
Сводка и дальнейшие действия
Теперь вы должны понять основы написания фонового класса задач, регистрации фоновой задачи из приложения и способа распознавания приложения при завершении фоновой задачи. Вы также должны понять, как обновить манифест приложения, чтобы приложение успешно зарегистрировать фоновую задачу.
Примечание.
Скачайте пример фоновой задачи, чтобы просмотреть аналогичные примеры кода в контексте полного и надежного приложения UWP, использующего фоновые задачи.
Дополнительные сведения см. в следующих разделах, посвященных справочнику по API, концептуальной инструкции по фоновой задаче и более подробные инструкции по написанию приложений, использующих фоновые задачи.
См. также
Подробные инструкции по фоновым задачам
- Реагирование на системные события с помощью фоновых задач
- Регистрация фоновой задачи
- Задание условий выполнения фоновой задачи
- Использование триггера обслуживания
- Обработка отмененной фоновой задачи
- Отслеживание хода выполнения и завершения фоновых задач
- Запуск фоновой задачи по таймеру
- Создайте и зарегистрируйте фоновую задачу в процессе.
- Преобразование фоновой задачи вне процесса в фоновую задачу внутри процесса
Руководство по фоновой задаче
- Рекомендации по фоновым задачам
- Отладка фоновой задачи
- Как активировать приостановку, возобновление и фоновые события в приложениях UWP (при отладке)
Справочник по API фоновой задачи