학습
취소된 백그라운드 작업 처리
중요 API
영구 스토리지를 사용해서 취소 요청을 인식하고, 작업을 중지하고, 앱에 취소를 보고하는 백그라운드 작업을 생성하는 방법을 알아보세요.
이 항목에서는 백그라운드 작업 진입점으로 사용되는 Run 메서드를 비롯하여 백그라운드 작업 클래스를 이미 만들었다고 가정합니다. 백그라운드 작업 구축을 빠르게 시작하려면 Out-of-process 백그라운드 작업 생성 및 등록 또는 In-process 백그라운드 작업 생성 및 등록을 참조하세요. 조건 및 트리거에 대한 자세한 내용은 백그라운드 작업을 이용한 앱 지원을 참조하세요.
이 토픽은 In-process 백그라운드 작업에도 적용할 수 있습니다. 그러나 Run 메서드 대신 OnBackgroundActivated로 대체합니다. 백그라운드 작업이 포그라운드 앱과 동일한 프로세스로 실행 중입니다. 따라서 In-process 백그라운드 작업에서는 취소 사실을 전달할 수 있으므로, 영구 스토리지를 사용하여 취소 신호를 보낼 필요가 없습니다.
취소 이벤트를 처리할 메서드를 작성하세요.
참고
데스크톱을 제외한 모든 디바이스 제품군의 경우 디바이스의 메모리가 적어지면 백그라운드 작업이 종료될 수 있습니다. 메모리 부족 예외가 표시되지 않거나 앱이 이를 처리하지 않으면 백그라운드 작업이 경고 없이 OnCanceled 이벤트를 발생시키지 않고 종료됩니다. 이렇게 하면 포그라운드에서 앱의 사용자 환경을 보장할 수 있습니다. 백그라운드 작업은 이 시나리오를 처리하도록 설계되어야 합니다.
다음과 같이 OnCanceled라는 메서드를 만듭니다. 이 메서드는 백그라운드 작업에 대해 취소 요청을 할 때 Windows 런타임에서 호출하는 진입점입니다.
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.
}
_CancelRequested라는 플래그 변수를 백그라운드 작업 클래스에 추가합니다. 이 변수는 취소 요청이 이루어진 시기를 나타내는 데 사용됩니다.
volatile bool _CancelRequested = false;
private:
volatile bool m_cancelRequested;
private:
volatile bool CancelRequested;
1단계에서 만든 OnCanceled 메서드에서 _CancelRequested 플래그 변수를 true로 설정합니다.
전체 백그라운드 작업 샘플 OnCanceled 메서드는 _CancelRequested true로 설정하고 잠재적으로 유용한 디버그 출력을 씁니다.
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;
}
백그라운드 작업의 Run 메서드에서 작업을 시작하기 전에 OnCanceled 이벤트 처리기 메서드를 등록합니다. In-process 백그라운드 작업에서는 애플리케이션 초기화의 일환으로 이 등록을 수행할 수 있습니다. 예를 들면 다음 코드 줄을 사용합니다.
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
taskInstance.Canceled({ this, &ExampleBackgroundTask::OnCanceled });
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &ExampleBackgroundTask::OnCanceled);
취소 요청이 수신되면 백그라운드 작업을 수행하는 메서드는 _cancelRequested가 true로 설정되는 것을 인식하여 작업을 중지하고 종료해야 합니다. In-process 백그라운드 작업의 경우 이는 OnBackgroundActivated 메서드에서 반환을 의미합니다. Out-of-process 백그라운드 작업의 경우 이는 Run 메서드에서 반환을 의미합니다.
백그라운드 작업 클래스가 작동하는 동안 플래그 변수를 검사하려면 백그라운드 작업 클래스의 코드를 수정하세요. _cancelRequested가 true로 설정된 경우 작업을 중지합니다.
백그라운드 작업 샘플에는 백그라운드 작업이 취소될 경우 추기적 타이머 콜백을 중지하는 검사가 포함되어 있습니다.
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.
}
참고
위에 표시된 코드 샘플에서는 백그라운드 작업 진행률을 기록하는 데 사용 중인 IBackgroundTaskInstance.Progress 속성을 사용합니다. 진행률은 BackgroundTaskProgressEventArgs 클래스를 사용해서 앱으로 다시 보고됩니다.
작업을 중지한 후에 작업이 완료되었는지 취소되었는지 여부를 기록하도록 Run 메서드를 수정합니다. 이 단계는 백그라운드 작업이 취소되었을 때 프로세스 간에 통신할 방법이 필요하기 때문에 Out-of-process 백그라운드 작업에 적용됩니다. In-process 백그라운드 작업의 경우, 작업이 취소되었음을 나타내려면 애플리케이션과 상태를 공유하기만 하면 됩니다.
백그라운드 작업 샘플에서는 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();
}
백그라운드 작업 샘플을 다운로드하면 메서드의 맥락에서 이 코드 예제들을 확인할 수 있습니다.
설명을 위해 샘플 코드에서는 백그라운드 작업 샘플에서 Run 메서드와 콜백 타이머의 일부만 표시합니다.
컨텍스트에 대한 백그라운드 작업 샘플의 전체 Run 메서드 및 타이머 콜백 코드는 아래와 같습니다.
// 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);
}