이 항목은 아직 평가되지 않았습니다.- 이 항목 평가

Windows Phone의 백그라운드 에이전트 구현 방법

2013-12-05

적용 대상: Windows Phone 8 | Windows Phone OS 7.1

 

이 항목에서는 예약된 작업을 사용하여 백그라운드 에이전트를 등록하는 앱 구현에 대해 설명합니다.

다음 단계에서 예약된 작업을 사용하여 백그라운드 에이전트를 등록하는 간단한 앱을 만드는 과정을 안내합니다.

예약된 작업을 사용하는 앱을 만들려면

  1. Visual Studio 에서 새 Windows Phone 앱  프로젝트를 만듭니다. 이 템플릿은 Windows Phone 카테고리에 있습니다.

  2. 다음으로, 솔루션에 예약된 작업 프로젝트를 추가합니다. 파일 메뉴에서 추가->새 프로젝트...를 선택합니다. 새 프로젝트 추가 대화 상자에서 Windows Phone 예약된 작업 에이전트를 선택합니다. 기본 이름인 ScheduledTaskAgent1을 그대로 두고 확인을 클릭합니다.

  3. 그런 다음 포그라운드 앱 프로젝트에서 에이전트 프로젝트에 대한 참조를 추가해야 합니다. 솔루션 탐색기에서 포그라운드 앱 프로젝트를 클릭하여 선택합니다. 다음으로, 프로젝트 메뉴에서 참조 추가...를 선택합니다. 참조 추가 대화 상자에서 프로젝트 탭을 선택합니다. 에이전트 프로젝트인 ScheduledTaskAgent1을 선택하고 확인을 클릭합니다.

  4. 솔루션 탐색기에서 ScheduledTaskAgent1 프로젝트에서 ScheduledAgent.cs를 두 번 클릭하여 이 파일을 엽니다. 이 파일에는 기본 클래스 ScheduledTaskAgent에서 상속되는 단일 클래스(ScheduledAgent)의 정의가 포함되어 있습니다. 이 예제에서는 Shell 네임스페이스 및 System 네임스페이스에 대한 using 지시문을 파일 맨 위에 추가합니다.

    using Microsoft.Phone.Scheduler;
    using Microsoft.Phone.Shell;
    using System;
    
    
  5. 클래스에서는 메서드 하나, 즉 OnInvoke(ScheduledTask)가 구현됩니다. 예약된 작업을 실행하면 운영 체제에서 이 메서드를 호출합니다. 백그라운드 에이전트 실행 시 실행할 코드를 여기에 삽입합니다. 각 앱은 ScheduledTaskAgent를 한 번에 하나만 등록할 수 있지만, 이 에이전트를 리소스를 많이 사용하는 에이전트와 정기 에이전트로 모두 예약할 수는 있습니다. 앱에서 ResourceIntensiveTaskPeriodicTask를 모두 사용하는 경우 OnInvoke 메서드로 전달되는 ScheduledTask 개체의 유형을 확인하여 에이전트 호출 대상 작업을 파악한 다음 코드 실행 분기를 필요한 대로 생성할 수 있습니다. 에이전트 유형을 하나만 사용하는 경우에는 ScheduledTask 개체 유형을 확인할 필요가 없습니다. 이 예제에서는 에이전트가 OnInvoke에서 ShellToast 개체를 실행하여 에이전트 호출 대상인 예약된 작업의 유형을 나타냅니다. 이 알림을 통해 에이전트 실행 시기를 확인할 수 있습니다. 단, 포그라운드 앱이 실행 중인 동안에는 표시되지 않습니다.

    예약된 작업 코드가 완료되면 NotifyComplete()를 호출하여 더 이상 실행할 필요가 없음을 운영 체제에 알려야 합니다. 이렇게 하면 운영 체제에서 다른 에이전트를 예약하려고 합니다.

    protected override void OnInvoke(ScheduledTask task)
    {
      //TODO: Add code to perform your task in background
      string toastMessage = "";
    
      // If your application uses both PeriodicTask and ResourceIntensiveTask
      // you can branch your application code here. Otherwise, you don't need to.
      if (task is PeriodicTask)
      {
        // Execute periodic task actions here.
        toastMessage = "Periodic task running.";
      }
      else
      {
        // Execute resource-intensive task actions here.
        toastMessage = "Resource-intensive task running.";
      }
    
      // Launch a toast to show that the agent is running.
      // The toast will not be shown if the foreground application is running.
      ShellToast toast = new ShellToast();
      toast.Title = "Background Agent Sample";
      toast.Content = toastMessage;
      toast.Show();
    
      // If debugging is enabled, launch the agent again in one minute.
    #if DEBUG_AGENT
      ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
    #endif
    
      // Call NotifyComplete to let the system know the agent is done working.
      NotifyComplete();
    }
    
    

    에이전트를 실제 장치에서 실행하는 것보다 자주 실행할 수 있도록 LaunchForTest(String, TimeSpan) 메서드가 제공됩니다. 이 메서드는 앱 개발에만 사용됩니다. 개발 도구를 사용하여 배포된 앱에서만 작동합니다. 프로덕션 앱에서는 이 메서드에 대한 호출을 제거해야 합니다. 이 예제에서는 디버깅 기능과 프로덕션 기능을 쉽게 전환할 수 있도록 #if 블록 내에 호출을 삽입했습니다. 호출을 사용하도록 설정하려면 ScheduledAgent.cs 파일 맨 위에 다음 줄을 삽입합니다.

    #define DEBUG_AGENT
    
    
    
  6. 다음 몇몇 단계에서는 정기 에이전트 및 리소스를 많이 사용하는 에이전트의 사용 여부를 사용자가 설정할 수 있도록 포그라운드 앱이 수정됩니다. 먼저 MainPage.xaml 파일을 열고 "ContentPanel"이라는 Grid 요소 내에 다음 XAML 코드를 붙여 넣습니다.

    <StackPanel>
      <StackPanel  Orientation="Vertical" Name="PeriodicStackPanel" Margin="0,0,0,40">
        <TextBlock Text="Periodic Agent" Style="{StaticResource PhoneTextTitle2Style}"/>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="name: " Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding Name}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="is enabled" VerticalAlignment="Center"  Style="{StaticResource PhoneTextAccentStyle}"/>
          <CheckBox Name="PeriodicCheckBox" IsChecked="{Binding IsEnabled}" Checked="PeriodicCheckBox_Checked" Unchecked="PeriodicCheckBox_Unchecked"/>                       
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="is scheduled: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding IsScheduled}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="last scheduled time: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding LastScheduledTime}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="expiration time: " Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding ExpirationTime}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="last exit reason: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding LastExitReason}" />
        </StackPanel>
      </StackPanel>
      <StackPanel  Orientation="Vertical" Name="ResourceIntensiveStackPanel" Margin="0,0,0,40">
        <TextBlock Text="Resource-intensive Agent" Style="{StaticResource PhoneTextTitle2Style}"/>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="name: " Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding Name}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="is enabled" VerticalAlignment="Center"  Style="{StaticResource PhoneTextAccentStyle}"/>
          <CheckBox Name="ResourceIntensiveCheckBox" IsChecked="{Binding IsEnabled}" Checked="ResourceIntensiveCheckBox_Checked" Unchecked="ResourceIntensiveCheckBox_Unchecked"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="is scheduled: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding IsScheduled}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="last scheduled time: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding LastScheduledTime}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="expiration time: " Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding ExpirationTime}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="last exit reason: "  Style="{StaticResource PhoneTextAccentStyle}"/>
          <TextBlock Text="{Binding LastExitReason}" />
        </StackPanel>
      </StackPanel>
    </StackPanel>
    
    

    이 코드는 각 에이전트 유형에 각각 하나씩, 두 개의 컨트롤 집합을 추가합니다. 대부분의 컨트롤은 백그라운드 에이전트를 나타내는 ScheduledTask 개체에 바인딩될 텍스트 블록이므로, 이러한 개체의 속성을 확인할 수 있습니다. 또한 각 에이전트 유형의 확인란이 추가되어 각 에이전트의 사용 상태를 표시하고 사용자가 이 상태를 전환할 수 있습니다. CheckedUnchecked 이벤트에 대한 처리기도 추가됩니다. 이러한 처리기를 사용해 에이전트를 설정 및 해제합니다.

  7. MainPage.xaml.cs에서 Microsoft.Phone.Scheduler 네임스페이스에 대한 using 지시문을 페이지 맨 위에 추가합니다.

    using Microsoft.Phone.Scheduler;
    
    
  8. 각 에이전트 유형을 나타낼 클래스 변수 두 개를 만듭니다. 이러한 개체는 UI에 바인딩됩니다. 예약된 작업 서비스는 예약된 작업을 해당 Name 속성으로 고유하게 식별합니다. 에이전트에 사용할 이름을 포함하는 변수를 두 개 만듭니다. 최종 사용자가 백그라운드 에이전트를 사용하지 않도록 설정했는지 여부를 추적하는 부울 변수도 하나 만듭니다. 그런 다음 클래스 정의 내에 다음 코드 줄을 추가합니다.

    public partial class MainPage : PhoneApplicationPage
    {
      PeriodicTask periodicTask;
      ResourceIntensiveTask resourceIntensiveTask;
    
      string periodicTaskName = "PeriodicAgent";
      string resourceIntensiveTaskName = "ResourceIntensiveAgent";
      public bool agentsAreEnabled = true;
    
    
    
  9. 다음으로, StartPeriodicAgent라는 도우미 메서드를 구현합니다. 이 메서드는 먼저 Find(String) 메서드를 사용하여 지정된 이름의 PeriodicTask에 대한 참조를 가져옵니다. 예약된 작업 개체가 null이 아닌 경우 Remove(String)를 호출하여 시스템에서 에이전트 등록을 취소해야 합니다. 에이전트는 직접 업데이트할 수 없으며, 제거한 후에 추가해야 합니다. 다음으로, 새 PeriodicTask 개체를 만들고 생성자에서 해당 이름을 할당합니다. 그리고 Description 속성을 설정합니다. 이 속성은 정기 에이전트에 필요하며, 장치의 백그라운드 작업 설정 페이지에서 사용자에게 에이전트 설명을 표시하는 데 사용됩니다. 이제 Add(ScheduledAction)를 호출하여 정기 에이전트를 시스템에 등록합니다. 사용자가 앱에 백그라운드 에이전트를 사용하지 않도록 설정한 경우 Add를 호출하면 InvalidOperationException이 발생합니다. 그러므로 호출을 try 블록 내에 삽입해야 합니다. 호출되면 연결된 UI 요소의 데이터 컨텍스트를 설정하여 데이터 바인딩을 업데이트하고 개체 속성을 사용자에게 표시합니다. 호출에서 예외가 발생되는 경우 예외의 메시지 문자열을 확인합니다. 문자열이 "BNS 오류: 작업이 사용하지 않도록 설정되었습니다."인 경우에는 백그라운드 에이전트가 사용하지 않도록 설정되었다는 알림을 사용자에게 표시해야 합니다.

    이 메서드를 호출하고 1분 뒤 에이전트가 실행되도록 LaunchForTest(String, TimeSpan) 메서드가 포함되어 있습니다. 이 메서드의 이전 호출과 마찬가지로, 앱이 디버그 모드와 프로덕션 모드를 쉽게 전환할 수 있도록 이 메서드는 #if 블록 내에 삽입되어 있습니다. 이 메서드를 사용하도록 설정하려면 MainPage.xaml.cs 맨 위에 다음 줄을 포함합니다.

    #define DEBUG_AGENT
    
    

    다음 메서드를 MainPage 클래스 정의에 붙여 넣습니다.

    private void StartPeriodicAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      // Obtain a reference to the period task, if one exists
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule
      if (periodicTask != null)
      {
        RemoveAgent(periodicTaskName);
      }
    
      periodicTask = new PeriodicTask(periodicTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      periodicTask.Description = "This demonstrates a periodic task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(periodicTask);
        PeriodicStackPanel.DataContext = periodicTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
          PeriodicCheckBox.IsChecked = false;
        }
        
        if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
        {
          // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
          
        }
        PeriodicCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        PeriodicCheckBox.IsChecked = false;
      }
    }
    
    
  10. 다음으로, 리소스를 많이 사용하는 작업에 대한 시작 도우미 메서드를 구현합니다. 이 메서드는 ResourceIntensiveTask 클래스를 사용하여 에이전트를 예약하고 다른 이름을 사용한다는 점을 제외하면 정기 에이전트용 메서드와 동일합니다.

    private void StartResourceIntensiveAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule.
      if (resourceIntensiveTask != null)
      {
        RemoveAgent(resourceIntensiveTaskName);
      }
    
      resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      resourceIntensiveTask.Description = "This demonstrates a resource-intensive task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(resourceIntensiveTask);
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
          
        }
        ResourceIntensiveCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        ResourceIntensiveCheckBox.IsChecked = false;
      }
    
    
    }
    
    
  11. 부울 클래스 변수 ignoreCheckBoxEvents를 추가합니다. 이 변수를 사용하여 페이지 초기화 시 CheckBox 이벤트를 해제합니다.

    bool ignoreCheckBoxEvents = false;
    
    
  12. 이제 CheckBox 컨트롤의 CheckedUnchecked 이벤트에 이벤트 처리기를 추가합니다. 이러한 처리기는 이전 단계에서 만든 시작 및 중지 도우미 메서드를 호출합니다. ignoreCheckBoxEvents가 true이면 처리기는 아무런 작업도 수행하지 않고 반환됩니다.

    private void PeriodicCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents) 
        return;
      StartPeriodicAgent();
    }
    private void PeriodicCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
       return;
      RemoveAgent(periodicTaskName);
    }
    private void ResourceIntensiveCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      StartResourceIntensiveAgent();
    }
    private void ResourceIntensiveCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      RemoveAgent(resourceIntensiveTaskName);
    }
    
    
  13. RemoveAgent 도우미 메서드는 예외로 인해 앱이 종료되지 않도록 단순히 try 블록에서 Remove(String)를 호출합니다.

    private void RemoveAgent(string name)
    {
      try
      {
        ScheduledActionService.Remove(name);
      }
      catch (Exception)
      {
      }
    }
    
    
  14. 마지막 단계에서는 PhoneApplicationPage 클래스의 OnNavigatedTo(NavigationEventArgs) 메서드를 재정의합니다. 사용자가 페이지로 이동할 때마다 이 메서드를 호출합니다. CheckBox 이벤트 처리기가 실제로 사용하지 않게 설정되도록 ignoreCheckBoxEvents를 true로 설정합니다. 그런 다음 Find(String) 메서드를 사용하여 앱의 정기 에이전트와 리소스를 많이 사용하는 에이전트가 시스템에 등록되었는지 확인하고, 앱의 현재 상태를 반영하도록 CheckBox 컨트롤을 업데이트합니다. 마지막으로 ignoreCheckBoxEvents를 다시 false로 설정합니다.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      ignoreCheckBoxEvents = true;
    
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      if (periodicTask != null)
      {
        PeriodicStackPanel.DataContext = periodicTask;
      }
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
      if (resourceIntensiveTask != null)
      {
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
      }
    
      ignoreCheckBoxEvents = false;
    
    }
    
    

이 정보가 도움이 되었습니까?
(1500자 남음)
의견을 주셔서 감사합니다.
표시:
© 2014 Microsoft. All rights reserved.