Freigeben über


Behandeln einer abgebrochenen Hintergrundaufgabe

Wichtige APIs

Hier erfahren Sie, wie Sie eine Hintergrundaufgabe erstellen, die eine Abbruchanforderung erkennt, die Arbeit beendet und den Abbruch mithilfe des beständigen Speichers an die App meldet.

In diesem Thema wird davon ausgegangen, dass Sie bereits eine Hintergrundaufgabenklasse erstellt haben, einschließlich der Run-Methode , die als Einstiegspunkt für Hintergrundaufgaben verwendet wird. Informationen zum schnellen Erstellen einer Hintergrundaufgabe finden Sie unter Erstellen und Registrieren einer Hintergrundaufgabe außerhalb von Prozessen oder Erstellen und Registrieren einer Hintergrundaufgabe im Prozess. Ausführlichere Informationen zu Bedingungen und Triggern finden Sie unter "Unterstützen Ihrer App mit Hintergrundaufgaben".

Dieses Thema gilt auch für In-Process-Hintergrundaufgaben. Ersetzen Sie aber anstelle der Run-Methode "OnBackgroundActivated". Hintergrundaufgaben in Prozessen erfordern nicht, dass Sie beständigen Speicher verwenden, um den Abbruch zu signalisieren, da Sie den Abbruch mithilfe des App-Zustands kommunizieren können, da die Hintergrundaufgabe im selben Prozess wie Ihre Vordergrund-App ausgeführt wird.

Verwenden der OnCanceled-Methode zum Erkennen von Abbruchanforderungen

Schreiben Sie eine Methode zum Behandeln des Abbruchereignisses.

Hinweis

Bei allen Gerätefamilien mit Ausnahme des Desktops können Hintergrundaufgaben beendet werden, wenn das Gerät nicht genügend Arbeitsspeicher hat. Wenn eine Ausnahme außerhalb des Arbeitsspeichers nicht angezeigt wird oder die App sie nicht behandelt, wird die Hintergrundaufgabe ohne Warnung und ohne Auslösen des OnCanceled-Ereignisses beendet. Dies trägt dazu bei, die Benutzererfahrung der App im Vordergrund sicherzustellen. Ihre Hintergrundaufgabe sollte so konzipiert sein, dass dieses Szenario behandelt wird.

Erstellen Sie eine Methode namens "OnCanceled " wie folgt. Diese Methode ist der Einstiegspunkt, der vom Windows-Runtime aufgerufen wird, wenn eine Abbruchanforderung für Ihre Hintergrundaufgabe erfolgt.

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

Fügen Sie der Hintergrundaufgabenklasse eine Flagvariable namens _CancelRequested hinzu. Diese Variable wird verwendet, um anzugeben, wann eine Abbruchanforderung vorgenommen wurde.

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

Legen Sie in der OnCanceled-Methode, die Sie in Schritt 1 erstellt haben, die Flagvariable _CancelRequested auf "true" fest.

Die OnCanceled-Methode für die vollständige Hintergrundaufgabe legt _CancelRequested auf "true" fest und schreibt potenziell nützliche Debugausgabe.

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

Registrieren Sie in der Run-Methode der Hintergrundaufgabe die OnCanceled-Ereignishandlermethode, bevor Sie mit der Arbeit beginnen. In einer In-Process-Hintergrundaufgabe können Sie diese Registrierung als Teil der Anwendungsinitialisierung durchführen. Verwenden Sie beispielsweise die folgende Codezeile.

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

Behandeln des Abbruchs durch Beenden der Hintergrundaufgabe

Wenn eine Abbruchanforderung empfangen wird, muss Die Methode, die Hintergrundarbeit ausführt, beendet werden, indem erkannt wird, wann _cancelRequested auf "true" festgelegt ist. Bei In-Process-Hintergrundaufgaben bedeutet dies, dass sie von der OnBackgroundActivated-Methode zurückgegeben wird. Bei Hintergrundaufgaben außerhalb von Prozessen bedeutet dies, dass sie von der Run-Methode zurückgegeben wird.

Ändern Sie den Code der Hintergrundaufgabenklasse, um die Flagvariable während der Arbeit zu überprüfen. Wenn _cancelRequested auf "true" festgelegt wird, beenden Sie die Fortsetzung der Arbeit.

Das Beispiel für Hintergrundaufgaben enthält eine Überprüfung, die den regelmäßigen Timerrückruf beendet, wenn die Hintergrundaufgabe abgebrochen wird.

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

Hinweis

Das oben gezeigte Codebeispiel verwendet die IBackgroundTaskInstance.Statuseigenschaft, die zum Aufzeichnen des Fortschritts von Hintergrundaufgaben verwendet wird. Der Fortschritt wird mithilfe der BackgroundTaskProgressEventArgs-Klasse an die App zurück gemeldet.

Ändern Sie die Run-Methode so, dass nach dem Beenden der Arbeit erfasst wird, ob die Aufgabe abgeschlossen oder abgebrochen wurde. Dieser Schritt gilt für Hintergrundaufgaben außerhalb von Prozessen, da Sie eine Möglichkeit benötigen, zwischen Prozessen zu kommunizieren, wenn die Hintergrundaufgabe abgebrochen wurde. Bei In-Process-Hintergrundaufgaben können Sie einfach den Status für die Anwendung freigeben, um anzugeben, dass die Aufgabe abgebrochen wurde.

Im Beispiel für die Hintergrundaufgabe wird der Status in LocalSettings aufgezeichnet.

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

Hinweise

Sie können das Hintergrundaufgabenbeispiel herunterladen, um diese Codebeispiele im Kontext von Methoden anzuzeigen.

Zur Veranschaulichung zeigt der Beispielcode nur Teile der Run-Methode (und des Rückruftimers) aus dem Hintergrundaufgabenbeispiel an.

Run-Methode (Beispiel)

Die vollständige Run-Methode und der Timerrückrufcode aus dem Beispiel für Hintergrundaufgaben werden unten für den Kontext angezeigt.

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