How to handle a cancelled background task (XAML)
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
Learn how to make a background task that recognizes cancellation requests and stops work, reporting the cancellation to the app using persistent storage.
Note For Windows Phone Store apps, if the device becomes low on memory, background tasks may be terminated without any 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.
What you need to know
Technologies
Prerequisites
- 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 Quickstart: Create and register a background task. For more in-depth information on conditions and triggers, see Support your app with background tasks.
Instructions
Step 1: Use the OnCanceled method to recognize cancellation requests
Write a method to handle the cancellation event.
Create a method named OnCanceled that has the following footprint. This method is the entry point called by the Windows Runtime whenever a cancellation request is made against your background task.
The OnCanceled method needs to have the following footprint:
private void OnCanceled(IBackgroundTaskInstance sender, 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 CancelRequested;
In the OnCanceled method you created in step 1, set the flag variable _CancelRequested to 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 SampleBackgroundTask::OnCanceled(IBackgroundTaskInstance^ taskInstance, BackgroundTaskCancellationReason reason) { // // Indicate that the background task is canceled. // CancelRequested = true; }
In the background task's Run method, register the OnCanceled event handler method before starting work.
For example, use the following line of code:
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &SampleBackgroundTask::OnCanceled);
Step 2: Handle cancellation by exiting the Run method
When a cancellation request is received, the Run method needs to stop work and exit by recognizing when _cancelRequested is set to true.
Modify the code of your background task class to check the flag variable while it's working. If _cancelRequested 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 ((CancelRequested == false) && (Progress < 100)) { Progress += 10; TaskInstance->Progress = Progress; } else { PeriodicTimer->Cancel(); // TODO: Record whether the task completed or was cancelled. }
Note The code sample shown above uses the IBackgroundTaskInstance.Progress property being used to record background task progress. This lets the Windows Runtime report progress back to the app using the BackgroundTaskProgressEventArgs class.
Modify the Run method so that after work has stopped, it records whether the task completed or was cancelled.
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 ((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(); }
Remarks
You can download the background task sample to see these code examples in the context of methods.
For illustrative purposes, the sample code above shows only portions of the Run method (and callback timer) from the background task sample.
Run method example
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...");
//
// 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.FromMilliseconds(500));
}
//
// 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.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();
}
}
void SampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
//
// Associate a cancellation handler with the background task.
//
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &SampleBackgroundTask::OnCanceled);
//
// Get the deferral object from the task instance, and take a reference to the taskInstance.
//
Deferral = 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" : "Completed");
//
// Indicate that the background task has completed.
//
Deferral->Complete();
}
};
TimeSpan period;
period.Duration = 500 * 10000; // 500 milliseconds
PeriodicTimer = ThreadPoolTimer::CreatePeriodicTimer(ref new TimerElapsedHandler(timerDelegate), period);
}
Related topics
Quickstart: Create and register a background task
How to register a background task
How to debug a background task
How to get a list of pending background tasks
How to monitor background task progress and completion
How to declare background tasks in the application manifest