情報
要求されたトピックは次のとおりです。しかし、このトピックはこのライブラリには含まれていません。

Windows Phone 8 のバックグラウンド エージェントを実装する方法

2014/06/18

対象: Windows Phone 8 および Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

 

このトピックでは、スケジュールされたタスクを使用してバックグラウンド エージェントを登録するアプリの実装について説明します。

以下の手順では、スケジュールされたタスクを使用してバックグラウンド エージェントを登録する単純なアプリの作成について説明します。

スケジュールされたタスクを使用するアプリを作成するには

  1. Visual Studio で、新しい Windows Phone アプリ  プロジェクトを作成します。このテンプレートは、Windows Phone カテゴリにあります。

  2. 次に、スケジュールされたタスク プロジェクトをソリューションに追加します。[ファイル] メニューの [追加]、[新しいプロジェクト] を選択します。[新しいプロジェクトの追加] ダイアログで、[Windows Phone のスケジュールされたタスク エージェント] を選択します。既定の名前である ScheduledTaskAgent1 をそのままにして、[OK] をクリックします。

  3. 次に、フォアグラウンド アプリケーション プロジェクトで、エージェント プロジェクトへの参照を追加する必要があります。ソリューション エクスプローラーで、フォアグラウンド アプリケーション プロジェクトをクリックして選択します。次に、[プロジェクト] メニューの [参照の追加] をクリックします。[参照の追加] ダイアログ ボックスで、[プロジェクト] タブを選択します。エージェント プロジェクトである ScheduledTaskAgent1 を選択し、[OK] をクリックします。

  4. アプリのマニフェスト ファイルを更新して、バックグラウンド エージェントに登録します。ソリューション エクスプローラーの [プロパティ] で、WMAppManifest.xml ファイルをダブルクリックし、[コードの表示] をクリックします。次のコードに示すように、Tasks 要素内で、DefaultTask 要素の後に ExtendedTask 要素を追加します。Specifier 属性の値は、ScheduledTaskAgent であることが必要です。他の属性は、前の手順でプロジェクトに追加した時に、スケジュールされたタスク エージェントに与えた名前を反映する必要があります。

    <Tasks> 
      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" /> 
      <ExtendedTask Name="BackgroundTask"> 
        <BackgroundServiceAgent Specifier="ScheduledTaskAgent" Name="ScheduledTaskAgent1" Source="ScheduledTaskAgent1" Type="ScheduledTaskAgent1.ScheduledAgent" /> 
      </ExtendedTask> 
    </Tasks> 
    
    
  5. ソリューション エクスプローラーで、ScheduledTaskAgent1 プロジェクトの下にある ScheduledAgent.cs をダブルクリックし、このファイルを開きます。このファイルには、基本クラス ScheduledTaskAgent から継承された ScheduledAgent という単一クラスの定義が含まれています。この例では、Shell 名前空間および System 名前空間の using ディレクティブをファイルの先頭に追加します。

    using Microsoft.Phone.Scheduler;
    using Microsoft.Phone.Shell;
    using System;
    
    
  6. このクラスには OnInvoke(ScheduledTask) という 1 つのメソッドが実装されています。このメソッドは、スケジュールされたタスクが起動されたときにオペレーティング システムによって呼び出されます。これは、バックグラウンド エージェントが動作したときに実行するコードを配置する場所です。各アプリは一度に 1 つの ScheduledTaskAgent のみを登録できますが、このエージェントは、リソースを大量に消費するエージェント、および定期的なエージェントの両方としてスケジュールできます。アプリで ResourceIntensiveTask および PeriodicTask の両方を使用する場合は、OnInvoke メソッドに渡される ScheduledTask オブジェクトの種類を確認して、エージェントの呼び出し対象のタスクを判断し、必要に応じてコードの実行を分岐できます。1 種類のエージェントのみを使用している場合は、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
    
    
  7. 次のいくつかの手順では、フォアグラウンド アプリを変更し、ユーザーが定期的なエージェントやリソースを大量に消費するエージェントを有効または無効にできるようにします。最初に、MainPage.xaml ファイルを開き、次の XAML コードを "ContentPanel" という名前の Grid 要素内に貼り付けます。

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

    このコードは、エージェントの種類ごとに 1 つずつ、合計 2 セットのコントロールを追加します。ほとんどのコントロールは、バックグラウンド エージェントを表す ScheduledTask オブジェクトにバインドされるテキスト ブロックであり、これらのオブジェクトのプロパティを表示する目的で使用できます。また、各エージェントの有効化のステータスを表示し、ユーザーによるステータス切り替えを可能にするチェック ボックスが、エージェントの種類ごとに追加されます。 Checked および Unchecked イベントにハンドラーが追加され、これらを使用してエージェントのオンとオフを切り替えることができます。

  8. MainPage.xaml.cs で、Microsoft.Phone.Scheduler 名前空間の using ディレクティブをページの先頭に追加します。

    using Microsoft.Phone.Scheduler;
    
    
  9. 各エージェントの種類を表す 2 つのクラス変数を作成します。これらは、UI にバインドされるオブジェクトです。スケジュールされたアクション サービスは、タスクの Name プロパティを使用し、スケジュールされたタスクを一意に識別します。エージェントに使用される名前を含む 2 つの変数を作成します。バックグラウンド エージェントがエンド ユーザーによって無効化されたかどうかを追跡する 1 つのブール変数を作成します。クラス定義の内部に次のコードを追加します。

    public partial class MainPage : PhoneApplicationPage
    {
      PeriodicTask periodicTask;
      ResourceIntensiveTask resourceIntensiveTask;
    
      string periodicTaskName = "PeriodicAgent";
      string resourceIntensiveTaskName = "ResourceIntensiveAgent";
      public bool agentsAreEnabled = true;
    
    
    
  10. 次に、StartPeriodicAgent という名前のヘルパー メソッドを実装します。このメソッドは最初に Find(String) メソッドを使用して、指定された名前の PeriodicTask への参照を取得します。スケジュールされたタスク オブジェクトが null でない場合は、Remove(String) を呼び出してシステムへのエージェントの登録を解除します。エージェントを直接更新することはできません。削除してから追加する必要があります。削除してから追加する必要があります。次に、新しい PeriodicTask オブジェクトを作成し、コンストラクターでその名前を割り当てます。次に、Description プロパティを設定します。定期的なエージェントではこのプロパティが必要であり、デバイスのバックグラウンドタスクの設定ページでユーザーに対するエージェントの説明として使用されます。次に、Add(ScheduledAction) を呼び出して定期的なエージェントをシステムに登録します。ユーザーがアプリに対してバックグラウンド エージェントを無効にした場合、Add を呼び出すと InvalidOperationException がスローされます。このため、呼び出しは try ブロックに配置する必要があります。呼び出しに成功した場合は、関連する UI 要素のデータ コンテキストを設定し、データ バインドを更新してオブジェクトのプロパティをユーザーに表示します。呼び出しで例外がスローされる場合は、例外のメッセージ文字列を確認します。"BNS Error: The action is disabled" (BNS エラー: アクションは無効です) という文字列の場合は、バックグラウンド エージェントが無効になったことをユーザーに警告する必要があります。

    LaunchForTest(String, TimeSpan) メソッドを含めることで、このメソッドを呼び出した 1 分後にエージェントが起動されます。このメソッドの前の呼び出しと同じように、#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;
      }
    }
    
    
  11. 次に、リソースを大量に消費するタスクの起動ヘルパー メソッドを実装します。このメソッドは、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;
      }
    
    
    }
    
    
  12. ブール クラス変数 ignoreCheckBoxEvents を追加します。この変数は、ページの初期化時に CheckBox イベントをオフにするために使用されます。

    bool ignoreCheckBoxEvents = false;
    
    
  13. 次に、CheckBox コントロールの Checked イベントおよび Unchecked イベントに対応するイベント ハンドラーを追加します。これらのハンドラーは、前の手順で作成した起動および停止ヘルパー メソッドを呼び出します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);
    }
    
    
  14. RemoveAgent ヘルパー メソッドは、try ブロックで単純に Remove(String) を呼び出し、例外によってアプリが終了しないようにします。

    private void RemoveAgent(string name)
    {
      try
      {
        ScheduledActionService.Remove(name);
      }
      catch (Exception)
      {
      }
    }
    
    
  15. 最後の手順は PhoneApplicationPage クラスの OnNavigatedTo(NavigationEventArgs) メソッドをオーバーライドすることです。このメソッドは、ユーザーがこのページに移動したときに常に呼び出されます。ignoreCheckBoxEvents を true に設定し、CheckBox のイベント ハンドラーが実質的に無効になるようにします。次に、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;
    
    }
    
    

表示: