Menangani tugas latar belakang yang dibatalkan

API penting

Pelajari cara membuat tugas latar belakang yang mengenali permintaan pembatalan, menghentikan pekerjaan, dan melaporkan pembatalan ke aplikasi menggunakan penyimpanan persisten.

Topik ini mengasumsikan Anda telah membuat kelas tugas latar belakang, termasuk metode Jalankan yang digunakan sebagai titik entri tugas latar belakang. Untuk memulai dengan cepat membangun tugas latar belakang, lihat Membuat dan mendaftarkan tugas latar belakang di luar proses atau Membuat dan mendaftarkan tugas latar belakang dalam proses. Untuk informasi lebih mendalam tentang kondisi dan pemicu, lihat Mendukung aplikasi Anda dengan tugas latar belakang.

Topik ini juga berlaku untuk tugas latar belakang dalam proses. Tetapi alih-alih metode Jalankan , ganti OnBackgroundActivated. Tugas latar belakang dalam proses tidak mengharuskan Anda menggunakan penyimpanan persisten untuk memberi sinyal pembatalan karena Anda dapat mengomunikasikan pembatalan menggunakan status aplikasi karena tugas latar belakang berjalan dalam proses yang sama dengan aplikasi latar depan Anda.

Gunakan metode OnCanceled untuk mengenali permintaan pembatalan

Tulis metode untuk menangani peristiwa pembatalan.

Catatan

Untuk semua keluarga perangkat kecuali desktop, jika perangkat menjadi rendah memori, tugas latar belakang dapat dihentikan. Jika pengecualian kehabisan memori tidak muncul, atau aplikasi tidak menanganinya, maka tugas latar belakang akan dihentikan tanpa peringatan dan tanpa menaikkan peristiwa OnCanceled. Ini membantu memastikan pengalaman pengguna aplikasi di latar depan. Tugas latar belakang Anda harus dirancang untuk menangani skenario ini.

Buat metode bernama OnCanceled sebagai berikut. Metode ini adalah titik masuk yang dipanggil oleh Windows Runtime ketika permintaan pembatalan dibuat terhadap tugas latar belakang Anda.

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

Tambahkan variabel bendera yang disebut _CancelRequested ke kelas tugas latar belakang. Variabel ini akan digunakan untuk menunjukkan kapan permintaan pembatalan telah dibuat.

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

Dalam metode OnCanceled yang Anda buat di langkah 1, atur variabel bendera _CancelRequested ke true.

Sampel tugas latar belakang lengkap metode OnCanceled mengatur _CancelRequested ke true dan menulis output debug yang berpotensi berguna.

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

Dalam metode Jalankan tugas latar belakang, daftarkan metode penanganan aktivitas OnCanceled sebelum memulai pekerjaan. Dalam tugas latar belakang dalam proses, Anda mungkin melakukan pendaftaran ini sebagai bagian dari inisialisasi aplikasi Anda. Misalnya, gunakan baris kode berikut.

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

Menangani pembatalan dengan keluar dari tugas latar belakang Anda

Ketika permintaan pembatalan diterima, metode Anda yang melakukan pekerjaan latar belakang perlu berhenti bekerja dan keluar dengan mengenali saat _cancelRequested diatur ke true. Untuk tugas latar belakang dalam proses, ini berarti kembali dari metode OnBackgroundActivated . Untuk tugas latar belakang di luar proses, ini berarti kembali dari metode Jalankan .

Ubah kode kelas tugas latar belakang Anda untuk memeriksa variabel bendera saat berfungsi. Jika _cancelRequested diatur ke true, hentikan pekerjaan agar tidak melanjutkan.

Sampel tugas latar belakang menyertakan pemeriksaan yang menghentikan panggilan balik timer berkala jika tugas latar belakang dibatalkan.

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

Catatan

Sampel kode yang ditunjukkan di atas menggunakan IBackgroundTaskInstance. Properti kemajuan sedang digunakan untuk merekam kemajuan tugas latar belakang. Kemajuan dilaporkan kembali ke aplikasi menggunakan kelas BackgroundTaskProgressEventArgs .

Ubah metode Jalankan sehingga setelah pekerjaan berhenti, ia merekam apakah tugas selesai atau dibatalkan. Langkah ini berlaku untuk tugas latar belakang di luar proses karena Anda memerlukan cara untuk berkomunikasi antar proses ketika tugas latar belakang dibatalkan. Untuk tugas latar belakang dalam proses, Anda cukup berbagi status dengan aplikasi untuk menunjukkan tugas dibatalkan.

Sampel tugas latar belakang mencatat status di 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();
}

Keterangan

Anda dapat mengunduh sampel tugas latar belakang untuk melihat contoh kode ini dalam konteks metode.

Untuk tujuan ilustrasi, kode sampel hanya memperlihatkan bagian dari metode Jalankan (dan timer panggilan balik) dari sampel tugas latar belakang.

Contoh metode eksekusi

Metode Jalankan lengkap, dan kode panggilan balik timer, dari sampel tugas latar belakang ditunjukkan di bawah ini untuk konteks.

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