Обработка отмененной фоновой задачи
Важные API
Узнайте, как сделать фоновую задачу, которая распознает запрос отмены, останавливает работу и сообщает об отмене приложению с помощью постоянного хранилища.
В этом разделе предполагается, что вы уже создали класс фоновой задачи, включая метод Run , используемый в качестве точки входа фоновой задачи. Чтобы быстро приступить к созданию фоновой задачи, см. статью "Создание и регистрация фоновой задачи вне процесса" или "Создание и регистрация фоновой задачи в процессе". Дополнительные сведения об условиях и триггерах см. в статье "Поддержка приложения с помощью фоновых задач".
Этот раздел также применим к фоновым задачам внутри процесса. Но вместо метода Run замените OnBackgroundActivated. Фоновые задачи внутри процесса не требуют использования постоянного хранилища для сигнала об отмене, так как вы можете сообщить об отмене с помощью состояния приложения, так как фоновая задача выполняется в том же процессе, что и приложение переднего плана.
Использование метода OnCanceled для распознавания запросов на отмену
Напишите метод для обработки события отмены.
Примечание.
Для всех семейств устройств, кроме настольных компьютеров, если устройство становится низким в памяти, фоновые задачи могут быть завершены. Если исключение из памяти не отображается или приложение не обрабатывает его, фоновая задача будет завершена без предупреждения и без вызова события OnCanceled. Это помогает обеспечить взаимодействие с пользователем приложения на переднем плане. Фоновая задача должна быть разработана для обработки этого сценария.
Создайте метод с именем OnCanceled следующим образом. Этот метод является точкой входа, вызываемой среда выполнения Windows при выполнении запроса на отмену в фоновой задаче.
private void OnCanceled(
IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
// TODO: Add code to notify the background task that it is cancelled.
}
void ExampleBackgroundTask::OnCanceled(
Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance,
Windows::ApplicationModel::Background::BackgroundTaskCancellationReason reason)
{
// TODO: Add code to notify the background task that it is cancelled.
}
void ExampleBackgroundTask::OnCanceled(
IBackgroundTaskInstance^ taskInstance,
BackgroundTaskCancellationReason reason)
{
// TODO: Add code to notify the background task that it is cancelled.
}
Добавьте переменную флага с именем _CancelRequested в класс фоновой задачи. Эта переменная будет использоваться для указания момента выполнения запроса на отмену.
volatile bool _CancelRequested = false;
private:
volatile bool m_cancelRequested;
private:
volatile bool CancelRequested;
В методе OnCanceled, созданном на шаге 1, задайте для переменной флага _CancelRequested значение true.
Полный пример фоновой задачи OnCanceled задает _CancelRequested значение true и записывает потенциально полезные выходные данные отладки.
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
// Indicate that the background task is canceled.
_cancelRequested = true;
Debug.WriteLine("Background " + sender.Task.Name + " Cancel Requested...");
}
void ExampleBackgroundTask::OnCanceled(
Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance,
Windows::ApplicationModel::Background::BackgroundTaskCancellationReason reason)
{
// Indicate that the background task is canceled.
m_cancelRequested = true;
}
void ExampleBackgroundTask::OnCanceled(IBackgroundTaskInstance^ taskInstance, BackgroundTaskCancellationReason reason)
{
// Indicate that the background task is canceled.
CancelRequested = true;
}
В методе Run фоновой задачи зарегистрируйте метод обработчика событий OnCanceled перед началом работы. В фоновой задаче внутри процесса вы можете выполнить эту регистрацию в рамках инициализации приложения. Например, используйте следующую строку кода.
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
taskInstance.Canceled({ this, &ExampleBackgroundTask::OnCanceled });
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &ExampleBackgroundTask::OnCanceled);
Обработка отмены путем выхода из фоновой задачи
При получении запроса на отмену метод, который выполняет фоновую работу, необходимо остановить работу и выйти, распознав, когда _cancelRequested задано значение true. Для фоновых задач в процессе это означает возвращение из метода OnBackgroundActivated . Для фоновых задач вне процесса это означает возвращение из метода Run .
Измените код фонового класса задач, чтобы проверить переменную флага во время работы. Если _cancelRequested становится равным true, остановите работу.
Пример фоновой задачи включает проверку, которая останавливает периодический обратный вызов таймера, если фоновая задача отменена.
if ((_cancelRequested == false) && (_progress < 100))
{
_progress += 10;
_taskInstance.Progress = _progress;
}
else
{
_periodicTimer.Cancel();
// TODO: Record whether the task completed or was cancelled.
}
if (!m_cancelRequested && m_progress < 100)
{
m_progress += 10;
m_taskInstance.Progress(m_progress);
}
else
{
m_periodicTimer.Cancel();
// TODO: Record whether the task completed or was cancelled.
}
if ((CancelRequested == false) && (Progress < 100))
{
Progress += 10;
TaskInstance->Progress = Progress;
}
else
{
PeriodicTimer->Cancel();
// TODO: Record whether the task completed or was cancelled.
}
Примечание.
В приведенном выше примере кода используется IBackgroundTaskInstance.Свойство Progress используется для записи хода выполнения фоновой задачи. Ход выполнения передается приложению с помощью класса BackgroundTaskProgressEventArgs.
Измените метод Run таким образом, чтобы после остановки работы он записывает, завершена ли задача или отменена. Этот шаг применяется к внепроцессным фоновым задачам, так как требуется способ взаимодействия между процессами при отмене фоновой задачи. Для фоновых задач в процессе можно просто поделиться состоянием с приложением, чтобы указать, что задача отменена.
Пример фоновой задачи записывает состояние записи в LocalSettings.
if ((_cancelRequested == false) && (_progress < 100))
{
_progress += 10;
_taskInstance.Progress = _progress;
}
else
{
_periodicTimer.Cancel();
var settings = ApplicationData.Current.LocalSettings;
var key = _taskInstance.Task.TaskId.ToString();
// Write to LocalSettings to indicate that this background task ran.
if (_cancelRequested)
{
settings.Values[key] = "Canceled";
}
else
{
settings.Values[key] = "Completed";
}
Debug.WriteLine("Background " + _taskInstance.Task.Name + (_cancelRequested ? " Canceled" : " Completed"));
// Indicate that the background task has completed.
_deferral.Complete();
}
if (!m_cancelRequested && m_progress < 100)
{
m_progress += 10;
m_taskInstance.Progress(m_progress);
}
else
{
m_periodicTimer.Cancel();
// Write to LocalSettings to indicate that this background task ran.
auto settings{ Windows::Storage::ApplicationData::Current().LocalSettings() };
auto key{ m_taskInstance.Task().Name() };
settings.Values().Insert(key, (m_progress < 100) ? winrt::box_value(L"Canceled") : winrt::box_value(L"Completed"));
// Indicate that the background task has completed.
m_deferral.Complete();
}
if ((CancelRequested == false) && (Progress < 100))
{
Progress += 10;
TaskInstance->Progress = Progress;
}
else
{
PeriodicTimer->Cancel();
// Write to LocalSettings to indicate that this background task ran.
auto settings = ApplicationData::Current->LocalSettings;
auto key = TaskInstance->Task->Name;
settings->Values->Insert(key, (Progress < 100) ? "Canceled" : "Completed");
// Indicate that the background task has completed.
Deferral->Complete();
}
Замечания
Вы можете скачать пример фоновой задачи, чтобы просмотреть эти примеры кода в контексте методов.
Для иллюстрирующих целей в примере кода показаны только части метода Run (и таймер обратного вызова) из примера фоновой задачи.
Пример метода Run
Полный метод Run и код обратного вызова таймера в примере фоновой задачи показан ниже для контекста.
// The Run method is the entry point of a background task.
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Background " + taskInstance.Task.Name + " Starting...");
// Query BackgroundWorkCost
// Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
// of work in the background task and return immediately.
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost"] = cost.ToString();
// Associate a cancellation handler with the background task.
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
// Get the deferral object from the task instance, and take a reference to the taskInstance;
_deferral = taskInstance.GetDeferral();
_taskInstance = taskInstance;
_periodicTimer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(PeriodicTimerCallback), TimeSpan.FromSeconds(1));
}
// Simulate the background task activity.
private void PeriodicTimerCallback(ThreadPoolTimer timer)
{
if ((_cancelRequested == false) && (_progress < 100))
{
_progress += 10;
_taskInstance.Progress = _progress;
}
else
{
_periodicTimer.Cancel();
var settings = ApplicationData.Current.LocalSettings;
var key = _taskInstance.Task.Name;
// Write to LocalSettings to indicate that this background task ran.
settings.Values[key] = (_progress < 100) ? "Canceled with reason: " + _cancelReason.ToString() : "Completed";
Debug.WriteLine("Background " + _taskInstance.Task.Name + settings.Values[key]);
// Indicate that the background task has completed.
_deferral.Complete();
}
}
void ExampleBackgroundTask::Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
{
// Query BackgroundWorkCost
// Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
// of work in the background task and return immediately.
auto cost{ Windows::ApplicationModel::Background::BackgroundWorkCost::CurrentBackgroundWorkCost() };
auto settings{ Windows::Storage::ApplicationData::Current().LocalSettings() };
std::wstring costAsString{ L"Low" };
if (cost == Windows::ApplicationModel::Background::BackgroundWorkCostValue::Medium) costAsString = L"Medium";
else if (cost == Windows::ApplicationModel::Background::BackgroundWorkCostValue::High) costAsString = L"High";
settings.Values().Insert(L"BackgroundWorkCost", winrt::box_value(costAsString));
// Associate a cancellation handler with the background task.
taskInstance.Canceled({ this, &ExampleBackgroundTask::OnCanceled });
// Get the deferral object from the task instance, and take a reference to the taskInstance.
m_deferral = taskInstance.GetDeferral();
m_taskInstance = taskInstance;
Windows::Foundation::TimeSpan period{ std::chrono::seconds{1} };
m_periodicTimer = Windows::System::Threading::ThreadPoolTimer::CreatePeriodicTimer([this](Windows::System::Threading::ThreadPoolTimer timer)
{
if (!m_cancelRequested && m_progress < 100)
{
m_progress += 10;
m_taskInstance.Progress(m_progress);
}
else
{
m_periodicTimer.Cancel();
// Write to LocalSettings to indicate that this background task ran.
auto settings{ Windows::Storage::ApplicationData::Current().LocalSettings() };
auto key{ m_taskInstance.Task().Name() };
settings.Values().Insert(key, (m_progress < 100) ? winrt::box_value(L"Canceled") : winrt::box_value(L"Completed"));
// Indicate that the background task has completed.
m_deferral.Complete();
}
}, period);
}
void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
// Query BackgroundWorkCost
// Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
// of work in the background task and return immediately.
auto cost = BackgroundWorkCost::CurrentBackgroundWorkCost;
auto settings = ApplicationData::Current->LocalSettings;
settings->Values->Insert("BackgroundWorkCost", cost.ToString());
// Associate a cancellation handler with the background task.
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &ExampleBackgroundTask::OnCanceled);
// Get the deferral object from the task instance, and take a reference to the taskInstance.
TaskDeferral = taskInstance->GetDeferral();
TaskInstance = taskInstance;
auto timerDelegate = [this](ThreadPoolTimer^ timer)
{
if ((CancelRequested == false) &&
(Progress < 100))
{
Progress += 10;
TaskInstance->Progress = Progress;
}
else
{
PeriodicTimer->Cancel();
// Write to LocalSettings to indicate that this background task ran.
auto settings = ApplicationData::Current->LocalSettings;
auto key = TaskInstance->Task->Name;
settings->Values->Insert(key, (Progress < 100) ? "Canceled with reason: " + CancelReason.ToString() : "Completed");
// Indicate that the background task has completed.
TaskDeferral->Complete();
}
};
TimeSpan period;
period.Duration = 1000 * 10000; // 1 second
PeriodicTimer = ThreadPoolTimer::CreatePeriodicTimer(ref new TimerElapsedHandler(timerDelegate), period);
}
См. также
- Создайте и зарегистрируйте фоновую задачу в процессе.
- Создание и регистрация внепроцессной фоновой задачи
- Объявление фоновых задач в манифесте приложения
- Рекомендации по фоновым задачам
- Отслеживание хода выполнения и завершения фоновых задач
- Регистрация фоновой задачи
- Реагирование на системные события с помощью фоновых задач
- Запуск фоновой задачи по таймеру
- Задание условий выполнения фоновой задачи
- Обновление живой плитки из фоновой задачи
- Использование триггера обслуживания
- Отладка фоновой задачи
- Как активировать приостановку, возобновление и фоновые события в приложениях UWP (при отладке)