Обработка отмененной фоновой задачиHandle a cancelled background task

Важные APIImportant APIs

Узнайте, как создать фоновую задачу, которая распознает запрос на отмену, прекращает работу и сообщает приложению об отмене, используя постоянное хранилище.Learn how to make a background task that recognizes a cancellation request, stops work, and reports the cancellation to the app using persistent storage.

В этом разделе предполагается, что вы уже создали класс фоновой задачи, включая метод Run , который используется в качестве точки входа фоновой задачи.This topic assumes you have already created a background task class, including the Run method that is used as the background task entry point. Чтобы быстро приступить к созданию фоновой задачи, выполните инструкции из раздела Создание и регистрация фоновой задачи, выполняемой вне процесса или Создание и регистрация фоновой задачи, выполняемой внутри процесса.To get started quickly building a background task, see Create and register an out-of-process background task or Create and register an in-process background task. Более углубленно с условиями и триггерами можно ознакомиться в разделе Поддержка приложения с помощью фоновых задач.For more in-depth information on conditions and triggers, see Support your app with background tasks.

Этот раздел также применим к фоновым задачам, выполняемым внутри процесса.This topic is also applicable to in-process background tasks. Но вместо метода Run замените онбаккграундактиватед.But instead of the Run method, substitute OnBackgroundActivated. В случае фоновых задач внутри процесса не требуется использовать постоянное хранилище для подачи сигнала об отмене, поскольку можно сообщить об отмене с помощью состояния приложения, так как фоновая задача выполняется в том же процессе, что и приложение переднего плана.In-process background tasks do not require you to use persistent storage to signal the cancellation because you can communicate the cancellation using app state since the background task is running in the same process as your foreground app.

Использование метода OnCanceled для распознавания запросов на отменуUse the OnCanceled method to recognize cancellation requests

Напишите метод, который будет обрабатывать событие отмены.Write a method to handle the cancellation event.

Примечание

Если устройству не хватает памяти, фоновые задачи могут быть завершены для всех семейств устройств (за исключением настольных компьютеров).For all device families except desktop, if the device becomes low on memory, background tasks may be terminated. Если исключение нехватки памяти не отображается или приложение не обрабатывает его, то фоновая задача будет завершена без предупреждения и без вызова события OnCanceled.If an out of memory exception is not surfaced, or the app doesn't handle it, then the background task will be terminated without warning and without raising the OnCanceled event. Это позволяет не прерывать работу пользователя с активным приложением.This helps to ensure the user experience of the app in the foreground. Ваша фоновая задача должна поддерживать этот сценарий.Your background task should be designed to handle this scenario.

Создайте метод с именем OnCanceled, как показано ниже.Create a method named OnCanceled as follows. Этот метод является точкой входа, вызываемой средой выполнения Windows при создании запроса на отмену для вашей фоновой задачи.This method is the entry point called by the Windows Runtime when a cancellation request is made against your background task.

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

Добавьте переменную флага с именем ** _ канцелрекуестед** в класс фоновой задачи.Add a flag variable called _CancelRequested to the background task class. Она будет использоваться для обозначения того, что был сделан запрос на отмену.This variable will be used to indicate when a cancellation request has been made.

volatile bool _CancelRequested = false;
private:
    volatile bool m_cancelRequested;
private:
    volatile bool CancelRequested;

В методе, который был создан на шаге 1, установите для переменной флага ** _ канцелрекуестед** значение true.In the OnCanceled method you created in step 1, set the flag variable _CancelRequested to true.

В примере uncanceled фоновой задачи Full для ** _ канцелрекуестед** задается значение true и записывается потенциально полезные выходные данные отладки.The full background task sample OnCanceled method sets _CancelRequested to true and writes potentially useful debug output.

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 фоновой задачи перед началом работы Зарегистрируйте метод обработчика событий, который следует отменять .In the background task's Run method, register the OnCanceled event handler method before starting work. В фоновой задаче внутри процесса можно выполнить эту регистрацию как часть инициализации приложения.In an in-process background task, you might do this registration as part of your application initialization. Например, используйте следующую строку кода.For example, use the following line of code.

taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
taskInstance.Canceled({ this, &ExampleBackgroundTask::OnCanceled });
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &ExampleBackgroundTask::OnCanceled);

Обработка отмены путем выхода из фоновой задачиHandle cancellation by exiting your background task

При получении запроса на отмену метод, который выполняет фоновую работу, должен прекратить работу и выйти, выполнив распознавание, если ** _ канцелрекуестед** имеет значение true.When a cancellation request is received, your method that does background work needs to stop work and exit by recognizing when _cancelRequested is set to true. Для выполняемых в процессе фоновых задач это означает возврат из метода онбаккграундактиватед .For in-process background tasks, this means returning from the OnBackgroundActivated method. Для незавершенных фоновых задач это означает возврат из метода Run .For out-of-process background tasks, this means returning from the Run method.

Измените код класса вашей фоновой задачи, чтобы проверить переменную флага во время выполнения задачи.Modify the code of your background task class to check the flag variable while it's working. Если ** _ канцелрекуестед** принимает значение true, продолжение работы невозможно.If _cancelRequested becomes set to true, stop work from continuing.

Образец фоновой задачи включает проверку, которая останавливает периодический обратный вызов таймера при отмене фоновой задачи.The background task sample includes a check that stops the periodic timer callback if the background task is canceled.

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

Примечание

В приведенном выше примере кода используется ибаккграундтаскинстанце. Свойство Progress , используемое для записи хода выполнения фоновой задачи.The code sample shown above uses the IBackgroundTaskInstance.Progress property being used to record background task progress. Сведения о ходе выполнения возвращаются приложению при помощи класса BackgroundTaskProgressEventArgs.Progress is reported back to the app using the BackgroundTaskProgressEventArgs class.

Измените метод Run , чтобы после того, как работа была остановлена, она зарегистрирует, была ли задача завершена или отменена.Modify the Run method so that after work has stopped, it records whether the task completed or was cancelled. Этот шаг относится к фоновым задачам, которые выполняются вне процесса, поскольку требуется способ связи между двумя процессами при отмене фоновой задачи.This step applies to out-of-process background tasks because you need a way to communicate between processes when the background task was cancelled. Чтобы указать, что задача была отменена для фоновых задач внутри процесса, можно просто использовать состояние совместно с приложением.For in-process background tasks, you can simply share state with the application to indicate the task was cancelled.

Образец фоновой задачи записывает состояние в LocalSettings.The background task sample records status in 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();
}

RemarksRemarks

Вы можете скачать пример фоновой задачи, чтобы увидеть эти примеры кода в контексте методов.You can download the background task sample to see these code examples in the context of methods.

В демонстрационных целях в образце кода показаны только части метода Run (и таймера обратного вызова) из примера фоновой задачи.For illustrative purposes, the sample code shows only portions of the Run method (and callback timer) from the background task sample.

Пример метода RunRun method example

Полный метод Run и код обратного вызова таймера из примера фоновой задачи показаны ниже для контекста.The complete Run method, and timer callback code, from the background task sample are shown below for context.

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