취소된 백그라운드 작업 처리

중요 API

영구 스토리지를 사용해서 취소 요청을 인식하고, 작업을 중지하고, 앱에 취소를 보고하는 백그라운드 작업을 생성하는 방법을 알아보세요.

이 항목에서는 백그라운드 작업 진입점으로 사용되는 Run 메서드를 비롯하여 백그라운드 작업 클래스를 이미 만들었다고 가정합니다. 백그라운드 작업 구축을 빠르게 시작하려면 Out-of-process 백그라운드 작업 생성 및 등록 또는 In-process 백그라운드 작업 생성 및 등록을 참조하세요. 조건 및 트리거에 대한 자세한 내용은 백그라운드 작업을 이용한 앱 지원을 참조하세요.

이 토픽은 In-process 백그라운드 작업에도 적용할 수 있습니다. 그러나 Run 메서드 대신 OnBackgroundActivated로 대체합니다. 백그라운드 작업이 포그라운드 앱과 동일한 프로세스로 실행 중입니다. 따라서 In-process 백그라운드 작업에서는 취소 사실을 전달할 수 있으므로, 영구 스토리지를 사용하여 취소 신호를 보낼 필요가 없습니다.

OnCanceled 메서드를 사용하여 취소 요청 인식하기

취소 이벤트를 처리할 메서드를 작성하세요.

참고 항목

데스크톱을 제외한 모든 디바이스 제품군의 경우 디바이스의 메모리가 적어지면 백그라운드 작업이 종료될 수 있습니다. 메모리 부족 예외가 표시되지 않거나 앱이 이를 처리하지 않으면 백그라운드 작업이 경고 없이 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 메서드에서는 _CancelRequestedtrue로 설정하고 잠재적으로 유용한 디버그 출력을 씁니다.

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

백그라운드 작업을 종료해서 취소 처리하기

취소 요청이 수신되면 백그라운드 작업을 수행하는 메서드는 _cancelRequestedtrue로 설정되는 것을 인식하여 작업을 중지하고 종료해야 합니다. 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);
}