Compartir a través de


Controlar una tarea en segundo plano cancelada

API importantes

Obtenga información sobre cómo realizar 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 mediante el almacenamiento persistente.

En este tema se supone que ya ha creado una clase de tarea en segundo plano, incluido el método Run que se usa como punto de entrada de tarea en segundo plano. Para empezar a crear rápidamente una tarea en segundo plano, consulte Crear y registrar una tarea en segundo plano fuera de proceso o Crear y registrar una tarea en segundo plano en proceso. Para obtener información más detallada sobre las condiciones y los desencadenadores, consulta Compatibilidad de la aplicación con tareas en segundo plano.

Este tema también es aplicable a las tareas en segundo plano en proceso. Pero en lugar del método Run , sustituya OnBackgroundActivated. Las tareas en segundo plano en proceso no requieren que use el almacenamiento persistente para indicar la cancelación porque puede comunicar la cancelación mediante el estado de la aplicación, ya que la tarea en segundo plano se ejecuta en el mismo proceso que la aplicación en primer plano.

Uso del método OnCanceled para reconocer solicitudes de cancelación

Escriba un método para controlar el evento de cancelación.

Nota:

Para todas las familias de dispositivos excepto el escritorio, si el dispositivo deja de estar en memoria, es posible que finalicen las tareas en segundo plano. Si no se muestra una excepción de memoria insuficiente o la aplicación no la controla, la tarea en segundo plano se finalizará sin advertencia y sin generar el evento OnCanceled. Esto ayuda a garantizar la experiencia del usuario de la aplicación en primer plano. La tarea en segundo plano debe diseñarse para controlar este escenario.

Cree un método denominado OnCanceled como se indica a continuación. Este método es el punto de entrada al que llama Windows Runtime cuando se realiza una solicitud de cancelación en la tarea en segundo plano.

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. Esta variable se usará para indicar cuándo se ha realizado una solicitud de cancelación.

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

En el método OnCanceled que creó en el paso 1, establezca la variable flag _CancelRequested en true.

El ejemplo de tarea en segundo plano completo OnCanceled establece _CancelRequested en true y escribe resultados de depuración potencialmente útiles.

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 Run de la tarea en segundo plano, registre el método de controlador de eventos OnCanceled antes de iniciar el trabajo. En una tarea en segundo plano en proceso, puede realizar este registro como parte de la inicialización de la aplicación. Por ejemplo, use la siguiente línea de código.

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

Controlar la cancelación mediante la salida de la tarea en segundo plano

Cuando se recibe una solicitud de cancelación, el método que realiza el trabajo en segundo plano debe detener el trabajo y salir al reconocer cuándo _cancelRequested se establece en true. Para las tareas en segundo plano en proceso, esto significa devolver desde el método OnBackgroundActivated . En el caso de las tareas en segundo plano fuera del proceso, esto significa devolver desde el método Run .

Modifique el código de la clase de tarea en segundo plano para comprobar la variable de marca mientras funciona. Si _cancelRequested se establece en true, deje de continuar el trabajo.

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.

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:

El ejemplo de código mostrado anteriormente usa IBackgroundTaskInstance. Propiedad Progress que se usa para registrar el progreso de la tarea en segundo plano. El progreso se notifica de nuevo a la aplicación mediante la clase BackgroundTaskProgressEventArgs.

Modifique el método Run para que después de que se haya detenido el trabajo, registra si la tarea se completó o se canceló. Este paso se aplica a las tareas en segundo plano fuera del proceso porque necesita una manera de comunicarse entre procesos cuando se canceló la tarea en segundo plano. En el caso de las tareas en segundo plano en proceso, simplemente puede compartir el estado con la aplicación para indicar que se canceló la tarea.

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

Comentarios

Puede descargar el ejemplo de tarea en segundo plano para ver estos ejemplos de código en el contexto de los métodos.

Para fines ilustrativos, el código de ejemplo muestra solo partes del método Run (y temporizador de devolución de llamada) del ejemplo de tarea en segundo plano.

Ejemplo del método Run

El método Run 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 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);
}