Controlar una tarea en segundo plano canceladaHandle a cancelled background task

API importantesImportant APIs

Aprende a crear una tarea en segundo plano que reconozca una solicitud de cancelación, detenga el trabajo e informe de la cancelación a la aplicación a través del almacenamiento persistente.Learn how to make a background task that recognizes a cancellation request, stops work, and reports the cancellation to the app using persistent storage.

En este tema se da por supuesto que ya ha creado una clase de tarea en segundo plano, incluido el método Run que se usa como punto de entrada de la tarea en segundo plano.This topic assumes you have already created a background task class, including the Run method that is used as the background task entry point. Para comenzar rápidamente a crear una tarea en segundo plano, consulta Crear y registrar una tarea en segundo plano fuera de proceso o Crear y registrar una tarea en segundo plano dentro de proceso.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. Para obtener información más detallada acerca de las condiciones y los desencadenadores, consulta Dar soporte a tu aplicación mediante tareas en segundo plano.For more in-depth information on conditions and triggers, see Support your app with background tasks.

Este tema también se aplica a las tareas en segundo plano dentro de proceso.This topic is also applicable to in-process background tasks. Pero en lugar del método Run , sustituya OnBackgroundActivated.But instead of the Run method, substitute OnBackgroundActivated. Las tareas en segundo plano dentro de proceso no requieren que uses almacenamiento persistente para señalar la cancelación porque puedes comunicarla con el estado de la aplicación dado que la tarea en segundo plano se ejecuta en el mismo proceso que la aplicación en primer plano.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.

Usar el método OnCanceled para reconocer solicitudes de cancelaciónUse the OnCanceled method to recognize cancellation requests

Escribe un método para controlar el evento de cancelación.Write a method to handle the cancellation event.

Nota

Para todas las familias de dispositivos excepto la de equipos de escritorio, si el dispositivo dispone de poca memoria, las tareas en segundo plano podrían finalizarse.For all device families except desktop, if the device becomes low on memory, background tasks may be terminated. Si no se encuentra una excepción de memoria insuficiente o la aplicación no la controla, la tarea en segundo plano finalizará sin previo aviso y sin provocar el evento OnCancel.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. Esto contribuye a garantizar la experiencia del usuario de la aplicación en primer plano.This helps to ensure the user experience of the app in the foreground. La tarea en segundo plano debe estar diseñada para controlar este escenario.Your background task should be designed to handle this scenario.

Cree un método denominado OnCancel como se indica a continuación.Create a method named OnCanceled as follows. Este método es el punto de entrada al que llama Windows Runtime cuando se realiza una solicitud de cancelación para la tarea en segundo plano.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.
}

Agregue una variable de marca denominada ** _ CancelRequested** a la clase de tarea en segundo plano.Add a flag variable called _CancelRequested to the background task class. Esta variable se usará para indicar que se ha realizado una solicitud de cancelación.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;

En el método OnCancel que creó en el paso 1, establezca la variable de marca ** _ CancelRequested** en true.In the OnCanceled method you created in step 1, set the flag variable _CancelRequested to true.

El método cancelado de ejemplo de tarea en segundo plano establece ** _ CancelRequested** en true y escribe una salida de depuración potencialmente útil.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;
}

En el método de ejecución de la tarea en segundo plano, registre el método de controlador de eventos OnCancel antes de iniciar el trabajo.In the background task's Run method, register the OnCanceled event handler method before starting work. En una tarea en segundo plano dentro de proceso, puedes hacer este registro como parte de la inicialización de la aplicación.In an in-process background task, you might do this registration as part of your application initialization. Por ejemplo, utilice la siguiente línea de código.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);

Controlar la cancelación al salir de la tarea en segundo planoHandle cancellation by exiting your background task

Cuando se recibe una solicitud de cancelación, el método que realiza el trabajo en segundo plano debe detener el trabajo y salir si se establece ** _ cancelRequested** en 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. En el caso de las tareas en segundo plano, esto significa que se devuelve desde el método OnBackgroundActivated .For in-process background tasks, this means returning from the OnBackgroundActivated method. En el caso de las tareas en segundo plano fuera de proceso, esto significa que se devuelve desde el método Run .For out-of-process background tasks, this means returning from the Run method.

Modifica el código de la clase de tarea en segundo plano para comprobar la variable de marca mientras está en funcionamiento.Modify the code of your background task class to check the flag variable while it's working. Si ** _ cancelRequested** se establece en true, deje de continuar el trabajo.If _cancelRequested becomes set to true, stop work from continuing.

El ejemplo de tarea en segundo plano incluye una comprobación que detiene la devolución de llamada del temporizador periódico si se cancela la tarea en segundo plano.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.
}

Nota

En el ejemplo de código mostrado anteriormente se usa IBackgroundTaskInstance. Propiedad de progreso que se usa para registrar el progreso de la tarea en segundo plano.The code sample shown above uses the IBackgroundTaskInstance.Progress property being used to record background task progress. El progreso se notifica a la aplicación mediante la clase BackgroundTaskProgressEventArgs.Progress is reported back to the app using the BackgroundTaskProgressEventArgs class.

Modifique el método Run para que después de que se detenga el trabajo, registre si la tarea se completó o se canceló.Modify the Run method so that after work has stopped, it records whether the task completed or was cancelled. Este paso se aplica a las tareas en segundo plano que se ejecutan fuera de proceso porque necesitas un medio de comunicación entre procesos cuando se cancela la tarea en segundo plano.This step applies to out-of-process background tasks because you need a way to communicate between processes when the background task was cancelled. Para tareas en segundo plano dentro de proceso, puedes compartir el estado con la aplicación para indicar que la tarea se canceló.For in-process background tasks, you can simply share state with the application to indicate the task was cancelled.

El ejemplo de tarea en segundo plano registra el estado en 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();
}

ObservacionesRemarks

Puedes descargar la muestra de tarea en segundo plano para ver estas muestras de código en contexto dentro de los métodos.You can download the background task sample to see these code examples in the context of methods.

Con fines ilustrativos, el código de ejemplo solo muestra partes del método Run (y del temporizador de devolución de llamada) del ejemplo de tarea en segundo plano.For illustrative purposes, the sample code shows only portions of the Run method (and callback timer) from the background task sample.

Ejemplo de método RunRun method example

El método de ejecución completo y el código de devolución de llamada del temporizador, del ejemplo de tarea en segundo plano , se muestran a continuación para el contexto.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);
}