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

方法: Windows Phone のアプリケーションの状態を維持および復元する

2012/02/09

Windows Phone の実行モデルでは、一度に 1 つのアプリケーションのみをフォアグラウンドで実行できます。ユーザーがアプリケーションを離れると、通常、そのアプリケーションは休止状態になります。休止状態では、アプリケーションのコードは実行されませんが、アプリケーションはメモリ内に残ります。ユーザーが [戻る] ボタンを押して休止状態のアプリケーションに戻ると、アプリケーションの実行が再開され、アプリケーションの状態が自動的に復元されます。ただし、ユーザーが別の操作に移った後、アプリケーションが廃棄状態にされる可能性があります。ユーザーが廃棄状態になったアプリケーションに戻る場合は、アプリケーションがメモリ内に存在しなくなっているため、アプリケーションでそれぞれの固有の状態を復元する必要があります。PhoneApplicationService クラスは、アプリケーションの状態を維持するのに役立つ 4 つのイベント (LaunchingActivatedDeactivated、および Closing) を提供します。これらのイベントを使用すると、アプリケーションの複数のページで使用されるグローバル アプリケーション データを復元することができます。このタイプのデータには、認証キーや Web サービス クエリの結果などがあります。このトピックでは、これらのイベントを使用してアプリケーションの状態の保存と復元を行うパターンについて説明します。アプリケーションの廃止状態の詳細については、「Windows Phone の実行モデル」を参照してください。

電話アプリケーションで Web サービスなどのネットワーク リソースから取得したデータを使用するのは一般的です。アプリケーション内の複数のページでネットワークからこのデータを使用することもよくあります。このデータはアプリケーションの状態の一部であると見なすことができます。アプリケーションを非アクティブにして再度アクティブ化すると、デバイス上にデータを保存している場合を除き、このデータは失われます。アプリケーションでは、ネットワーク リソースへのクエリを再度実行してデータを取得しなおす必要があります。ただし、アプリケーションが非アクティブになっているときに状態データを保存し、アプリケーションが再度アクティブ化されたときにそれを復元する方法は 2 つあります。最初の方法では、永続ストレージを使用します。これには、分離ストレージとローカル データベースが含まれます。PhoneApplicationServiceState ディクショナリは、アプリケーションが廃棄状態になっている間だけ維持される一時的な格納場所で、永続ストレージよりもはるかに高速にアクセスされます。これらの 2 種類のストレージを正しく使用すると、ユーザー エクスペリエンスやアプリケーションの読み込み時間に大きな効果があります。

重要な注重要な注:

State ディクショナリに格納するデータは、直接またはデータ コントラクトを使用してシリアル化可能である必要があります。詳細については、「データ コントラクトの使用」を参照してください。

このトピックでは、Web から取得したデータを表示するアプリケーションの作成について説明します。アプリケーションが廃棄され終了されると、アプリケーションが起動、非アクティブ化、廃棄、および再アクティブ化されるのに伴って、このデータは保存、復元、および取得されます。アプリケーションのユーザー インターフェイスの応答性を維持できるように、時間のかかるデータ操作はすべて非同期で実行されます。この例で使用されるアプリケーションの状態データは、Web サイトから取得され、ページの TextBlock に表示される簡単な文字列です。実際のアプリケーションでは、一般的により構造化されたデータと複雑なユーザー インターフェイスを使用しますが、基本的な考え方は同じです。

このトピックでは、2 つのパートでアプリケーションの状態管理の実装について説明します。最初に、メインのアプリケーション クラスを変更して、アプリケーションの状態イベントを処理します。次に、保存されたアプリケーションの状態を使用する PhoneApplicationPage オブジェクトの実装について説明します。

このセクションでは、サンプル アプリケーションを実装するために、メインのアプリケーション クラスに対して行う変更について説明します。

アプリケーション クラスを変更するには

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

  2. このセクションの手順では、すべての [Silverlight for Windows Phone] プロジェクト テンプレートに含まれる App.xaml.cs ファイルを変更します。最初に、次の using ディレクティブをファイルの先頭に追加します。

    using System.Threading;
    using System.IO;
    using System.IO.IsolatedStorage;
    
    
  3. 次のコードでは、パブリック プロパティ ApplicationDataObject が作成されます。このパブリック プロパティはアプリケーション データ (この例では、簡単な文字列オブジェクト) にアクセスするのに使用されますこのプロパティでは、プライベート変数 _applicationDataObject を使用してデータを格納します。ApplicationDataObjectChanged イベントと OnApplicationDataObjectChanged が作成され、アプリケーション内のページで、アプリケーション データが変更されるたびにイベントを受信してページの状態を再描画できるようになります。App.xaml.cs の App クラス定義内に次のコードを貼り付けます。

    // Declare a private variable to store application state.
    private string _applicationDataObject;
    
    // Declare an event for when the application data changes.
    public event EventHandler ApplicationDataObjectChanged;
    
    // Declare a public property to access the application data variable.
    public string ApplicationDataObject
    {
      get { return _applicationDataObject; }
      set
      {
        if (value != _applicationDataObject)
        {
          _applicationDataObject = value;
          OnApplicationDataObjectChanged(EventArgs.Empty);
        }
      }
    }
            
    // Create a method to raise the ApplicationDataObjectChanged event.
    protected void OnApplicationDataObjectChanged(EventArgs e)
    {
      EventHandler handler = ApplicationDataObjectChanged;
      if (handler != null)
      {
        handler(this, e);
      }
    }
    
    
  4. この例では、アプリケーション データをページに公開するのに、ApplicationDataObject プロパティを使用します。アプリケーション データのソース (Web、分離ストレージ、またはアプリケーションの State ディクショナリ) を公開するのに、独立したプロパティ (ApplicationDataStatus) を使用します。前の手順のコードの下に次のプロパティ定義を貼り付けます。

    // Declare a public property to store the status of the application data.
    public string ApplicationDataStatus { get; set; }
    
    
  5. 次に、アプリケーションのイベント ハンドラーを実装します。すべての [Silverlight for Windows Phone] プロジェクト テンプレートで、これらのイベント ハンドラーのスタブは App.xaml.cs で提供されます。Launching イベントは、電話のアプリケーション リストでアプリケーションのアイコンをクリックするなどして、ユーザーが最初にアプリケーションを起動したときに発生します。この例では、このイベント ハンドラーは空のままになり、アプリケーション状態の読み込みはコード内の別の場所で処理されます。アプリケーションはこの Launching でコードを実行することができますが、このイベント ハンドラーでコードが実行されると、アプリケーションの起動に遅延が生じます。このため、このイベント ハンドラーで時間のかかるリソースにアクセスしないようにする必要があります。これには、分離ストレージ、データベース ストレージ、または Web サービスへのアクセスなどが含まれます。すべてのアプリケーション イベントは、10 秒間の時間制限があります。アプリケーションが何らかの原因でこの制限を超えた場合、アプリケーションは直ちに終了されます。

    // Code to execute when the application is launching (for example, from Start)
    // This code will not execute when the application is reactivated.
    private void Application_Launching(object sender, LaunchingEventArgs e)
    {
    }
    
    
  6. Activated イベントは、アプリケーションが休止状態または廃棄状態で、ユーザーがアプリケーションに戻ったときに発生します。Launching イベントと同様に、このイベントで実行されるコードにより、アプリケーションの再開に遅延が生じます。このハンドラーで、分離ストレージやデータベース ストレージにアクセスしたり、同期 Web 要求を実行したりしないでください。ActivatedEventArgsIsApplicationInstancePreserved プロパティでは、アプリケーションが休止状態から戻るかどうか、または廃棄状態になったかどうかをアプリケーションに認識させることができます。これは主に、ページレベルの Silverlight イベントを受け取らない XNA Framework アプリケーションに役立ちます。この例では、ApplicationInstancePreserved が true (アプリケーションが休止状態になったため、その状態が自動的に保持されたことを表す) の場合に、ApplicationDataStatus プロパティが更新され、ハンドラーが終了します。次に、ハンドラーが State ディクショナリでアプリケーション データが利用可能かどうかを確認します。利用可能 (アプリケーションが Deactivated イベント時に廃棄されて保存された状態) である場合、State ディクショナリから ApplicationDataObject が読み込まれ、データ ステータス プロパティが更新されます。既存の Application_Activated ハンドラーを次のコードに置き換えます。

    // Code to execute when the application is activated (brought to the foreground)
    // This code will not execute when the application is first launched.
    private void Application_Activated(object sender, ActivatedEventArgs e)
    {
      if (e.IsApplicationInstancePreserved)
      {
        ApplicationDataStatus = "application instance preserved.";
        return;
      }
    
      // Check to see if the key for the application state data is in the State dictionary.
      if (PhoneApplicationService.Current.State.ContainsKey("ApplicationDataObject"))
      {
        // If it exists, assign the data to the application member variable.
        ApplicationDataStatus = "data from preserved state.";
        ApplicationDataObject = PhoneApplicationService.Current.State["ApplicationDataObject"] as string;
      }
    }
    
    
  7. Deactivated イベントは、ユーザーがナビゲーション機能で次に進んでアプリケーションを離れたときに呼び出されます。通常、非アクティブになったアプリケーションは休止状態になりますが、このイベントの後でアプリケーションが廃棄または終了されるかどうかをこの時点で把握する方法はありません。このため、アプリケーションの状態を State ディクショナリと分離ストレージまたはデータベース ストレージに保存する必要があります。この例のアプリケーションでは、ApplicationDataObjectState と IsolatedStorage に保存します。メソッド SaveDataToIsolatedStorage はヘルパー メソッドで、このトピックの後の部分で定義されます。すべてのアプリケーション イベントと同様に、このハンドラーの完了に要する時間が 10 秒を超える場合、アプリケーションは終了されます。このため、アプリケーションが有効である間は、状態を追加的に保存することをお勧めします。このイベントは、保存されていないデータを保存する最後の機会になります。App.xaml.cs の既存の Deactivated ハンドラーに次のコードを貼り付けます。

    // Code to execute when the application is deactivated (sent to background)
    // This code will not execute when the application is closing.
    private void Application_Deactivated(object sender, DeactivatedEventArgs e)
    {
      // If there is data in the application member variable...
      if (!string.IsNullOrEmpty(ApplicationDataObject))
      {
        // Store it in the State dictionary.
        PhoneApplicationService.Current.State["ApplicationDataObject"] = ApplicationDataObject;
    
        // Also store it in isolated storage, in case the application is never reactivated.
        SaveDataToIsolatedStorage("myDataFile.txt", ApplicationDataObject);
      }
    }
    
    
  8. Closing イベントは、ユーザーが [戻る] ボタンを使用してアプリケーションの最初のページより前に戻ったときに発生します。このイベントが発生すると、アプリケーションはします。ユーザーがアプリケーションに戻るには、アプリケーションを再起動する必要があります。このため、状態データを分離ストレージに保存する必要はありますが、State ディクショナリに保存する必要はありません。アプリケーションでこのイベントの完了に要する時間が 10 秒を超えると、アプリケーションは直ちに終了されます。そのため、アプリケーションが有効である間は、状態を追加的に保存することをお勧めします。App.xaml.cs の既存の Closing ハンドラーに次のコードを貼り付けます。

    // Code to execute when the application is closing (for example, the user pressed the Back button)
    // This code will not execute when the application is deactivated.
    private void Application_Closing(object sender, ClosingEventArgs e)
    {
      // The application will not be tombstoned, so save only to isolated storage.
      if (!string.IsNullOrEmpty(ApplicationDataObject))
      {
        SaveDataToIsolatedStorage("myDataFile.txt", ApplicationDataObject);
      }
    }
    
  9. 次に、いくつかのヘルパー メソッドを作成して、グローバル アプリケーション データを取得します。このコードをアプリケーション クラスに配置すると、アプリケーションのページごとに実装する必要がなくなります。最初に、アプリケーション ページから呼び出すことができるように、GetDataAsync メソッドがパブリックとして定義されます。これにより、新しいスレッドが作成され、GetData ヘルパー メソッドが呼び出されます。時間のかかるデータ操作は必ず非同期で実行し、アプリケーションのユーザー インターフェイスが常に応答する状態にする必要があります。

    この例では、分離ストレージに保存されるタイムスタンプを使用して、アプリケーション データが最後に保存された時刻を記録します。GetData ヘルパー メソッドはこのタイムスタンプを確認し、データが保存されてから 30 秒未満の場合に、アプリケーション データを分離ストレージから読み込んで ApplicationDataObject プロパティに格納します。以前は、ApplicationDataObject が変更されたときにリッスンしているすべてのページがイベントを受け取るように、ApplicationDataObjectChanged イベントが作成されました。また、ApplicationDataStatus フィールドが更新されるため、データが分離ストレージから取得されたことをページで表示することができます。

    タイム スタンプから分離ストレージ内のデータが 30 秒より前のものであることがわかると、アプリケーションは Web から新しいデータを取得します。この例では、HttpWebRequest を使用して、要求を開始します。ヘルパー メソッド HandleWebResponse は、次の手順に示されています。

    public void GetDataAsync()
    {
      // Call the GetData method on a new thread.
      Thread t = new Thread(new ThreadStart(GetData));
      t.Start();
    }
    
    private void GetData()
    {
      // Check the time elapsed since data was last saved to isolated storage.
      TimeSpan TimeSinceLastSave = TimeSpan.FromSeconds(0);
      if (IsolatedStorageSettings.ApplicationSettings.Contains("DataLastSavedTime"))
      {
        DateTime dataLastSaveTime = (DateTime)IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"];
        TimeSinceLastSave = DateTime.Now - dataLastSaveTime;
      }
    
      // Check to see if data exists in isolated storage and see if the data is fresh.
      // This example uses 30 seconds as the valid time window to make it easy to test. 
      // Real apps will use a larger window.
      IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
      if (isoStore.FileExists("myDataFile.txt") && TimeSinceLastSave.TotalSeconds < 30)
      {
        // This method loads the data from isolated storage, if it is available.
        StreamReader sr = new StreamReader(isoStore.OpenFile("myDataFile.txt", FileMode.Open));
        string data = sr.ReadToEnd();
        sr.Close();
    
        ApplicationDataStatus = "data from isolated storage";
        ApplicationDataObject = data;
      }
      else
      {
        // Otherwise, it gets the data from the web. 
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri("http://windowsteamblog.com/windows_phone/b/windowsphone/rss.aspx"));
        request.BeginGetResponse(HandleWebResponse, request);
      }
    }
    
    
  10. HandleWebResponse ヘルパー メソッドは、GetData で開始された Web 要求の結果を処理するのに使用されます。このメソッドは応答ストリームのデータを読み込もうとします。データの読み込みに成功すると、ApplicationDataObject 変数にデータが格納され、ApplicationDataObjectChanged イベントが当該イベントを登録しているページで発生します。また、データが Web から取得されたことを示す状態変数が設定されます。Web 要求が失敗した場合にも、状態は更新されます。

    private void HandleWebResponse(IAsyncResult result)
    {
      // Put this in a try block in case the web request was unsuccessful.
      try
      {
        // Get the request from the IAsyncResult.
        HttpWebRequest request = (HttpWebRequest)(result.AsyncState);
    
        // Read the response stream from the response.
        StreamReader sr = new StreamReader(request.EndGetResponse(result).GetResponseStream());
        string data = sr.ReadToEnd();
    
        // Use the Dispatcher to call SetData on the UI thread, passing the retrieved data.
        //Dispatcher.BeginInvoke(() => { SetData(data, "web"); });
        ApplicationDataStatus = "data from web.";
        ApplicationDataObject = data;
      }
      catch
      {
        // If the data request fails, alert the user.
        ApplicationDataStatus = "Unable to get data from Web.";
        ApplicationDataObject = “”;
      }
    }
    
    
  11. App.xaml.cs で実装する最後のヘルパー メソッドは SaveDataToIsolatedStorage です。このメソッドは、Deactivated および Closing イベント ハンドラーから呼び出されます。このメソッドは、提供された値を指定されたファイルに保存し、GetData でチェックされるタイムスタンプをリセットします。

    private void SaveDataToIsolatedStorage(string isoFileName, string value)
    {
      IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
      StreamWriter sw = new StreamWriter(isoStore.OpenFile(isoFileName, FileMode.OpenOrCreate));
      sw.Write(value);
      sw.Close();
      IsolatedStorageSettings.ApplicationSettings["DataLastSaveTime"] = DateTime.Now;
    }
    
    

このセクションでは、サンプル アプリケーションを実装するために、メインのページ クラスに対して行う変更について説明します。

ページ クラスを変更するには

  1. アプリケーションの状態を管理するようにアプリケーション クラスが変更されている場合、状態データを使用するのに各ページに必要なコードはわずかな量で済みます。最初に、TextBlock コントロールを 2 つ作成します。1 つはアプリケーション データを表示するのに使用され、もう 1 つはデータの取得場所を示す状態変数を表示するのに使用されます。実際のアプリケーションでは、使いやすさや注目度を重視してデータを示すことになりますが、この例では、簡単にするために、データをテキストとしてページ上に表示します。

    MainPage.xaml ファイルで、"ContentPanel" というの名前の Grid 要素に次の XAML コードを配置します。

    <TextBlock Height="30" HorizontalAlignment="Left" Margin="20,20,0,0" Name="statusTextBlock" Text="no status" VerticalAlignment="Top" Width="424" />
    <TextBlock HorizontalAlignment="Left" Margin="20,60,0,0" Name="dataTextBlock" Text="no data" VerticalAlignment="Top" Width="424" Foreground="{StaticResource PhoneAccentBrush}" MaxWidth="424" />
    
  2. 次に、_isNewPageInstance というブール変数を追加します。この変数は、ページが新しいインスタンスかどうかを判断するのにあとから使用されます。MainPage.xaml.cs の MainPage クラス定義に次の行を追加します。

    public partial class MainPage : PhoneApplicationPage
    {
            bool _isNewPageInstance = false;
    
    
  3. MainPage クラスのコンストラクターで、_isNewPageInstance を true に設定します。ユーザーがナビゲーション機能で既存のページに戻る場合、このコンストラクターは呼び出されません。この場合、状態を再度読み込む必要はありません。次に、App.xaml.cs で定義されている ApplicationDataObjectChanged イベント ハンドラーに登録します。これにより、アプリケーション データがいつ変更されたかをページで把握することができます。

    // Constructor
    public MainPage()
    {
      InitializeComponent();
    
      _isNewPageInstance = true;
    
      // Set the event handler for when the application data object changes.
      (Application.Current as ExecutionModelApplication.App).ApplicationDataObjectChanged +=
                    new EventHandler(MainPage_ApplicationDataObjectChanged);
    }
    
    
  4. OnNavigatedTo(NavigationEventArgs) メソッドは、ユーザーがページに移動したときに呼び出されます。_isNewPageInstance の値をチェックして、ページが新しいかどうか、またはユーザーが既にメモリ内にあるページに戻っているかどうかを確認します。ページが新しく、ApplicationDataObject 変数が null でない場合は、ページの UI の TextBox コントロールを更新するために後で定義する UpdateApplicationUI ヘルパー メソッドを呼び出します。ApplicationDataObject 変数が null の場合は、分離ストレージまたは Web のいずれかからデータを取得する必要があります。この場合、ユーザーがデータが取得されていることを把握できるように状態の TextBlock が更新され、App.xaml.cs で定義された GetDataAsync ヘルパー メソッドが呼び出されます。最後に、_isNewPageInstance が false に設定されます。

    次のメソッド定義を MainPage.xaml.cs に貼り付けます。

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      // If _isNewPageInstance is true, the page constructor has been called, so
      // state may need to be restored.
      if (_isNewPageInstance)
      {
        // If the application member variable is not empty,
        // set the page's data object from the application member variable.
        if ((Application.Current as ExecutionModelApplication.App).ApplicationDataObject != null)
        {
          UpdateApplicationDataUI();
        }
        else
        {
          // Otherwise, call the method that loads data.
          statusTextBlock.Text = "getting data...";
          (Application.Current as ExecutionModelApplication.App).GetDataAsync();
        }
      }
    
      // Set _isNewPageInstance to false. If the user navigates back to this page
      // and it has remained in memory, this value will continue to be false.
      _isNewPageInstance = false;
    }
    
    
  5. 最後に、ApplicationDataObjectChanged イベントのイベント ハンドラーを作成します。このメソッドは、UpdateApplicationDataUI を呼び出して、ページの UI を新しいデータに更新します。

    // The event handler called when the ApplicationDataObject changes.
    void MainPage_ApplicationDataObjectChanged(object sender, EventArgs e)
    {
      // Call UpdateApplicationData on the UI thread.
      Dispatcher.BeginInvoke(() => UpdateApplicationDataUI());      
    }
    void UpdateApplicationDataUI()
    {
      // Set the ApplicationData and ApplicationDataStatus members of the ViewModel
      // class to update the UI.
      dataTextBlock.Text = (Application.Current as ExecutionModelApplication.App).ApplicationDataObject;
      statusTextBlock.Text = (Application.Current as ExecutionModelApplication.App).ApplicationDataStatus;
    }
    
    

Windows Phone 7.5 では、フォアグラウンド アプリケーションを円滑に実行するために十分なメモリがある限り、アプリケーションはユーザーが別の操作に移った時点で休止状態にされます。アプリケーションが休止状態になった後に復元されたとき、UI の状態は自動的に保持されています。廃棄状態にされた後にページの状態が適切に復元されたことを確認するには、デバッガーで自動廃棄を有効にする必要があります。

[プロジェクト] メニューの [[アプリケーション名] のプロパティ] をクリックするか、ソリューション エクスプローラーでプロジェクトを右クリックし、[プロパティ] をクリックして、[プロジェクトのプロパティ] ダイアログを開きます。[デバッグ] タブで、[デバッグ時に非アクティブ化されたら廃棄状態にする] というラベルが付いたチェック ボックスをオンにします。

廃棄を有効にした後で、F5 キーを押してアプリケーションのデバッグを開始します。アプリケーションが初めて読み込まれたとき、分離ストレージやアプリケーションの State ディクショナリに状態データは存在しません。そのため、データが表示され、状態フィールドにはデータが Web から取得されたものであることが示されます。[スタート] ボタンを押してアプリケーションを非アクティブにしてから、[戻る] ボタンを押してアプリケーションを再度アクティブにすると、データはアプリケーションの State ディクショナリから取得されます。次に、[戻る] ボタンを再度押してアプリケーションを終了してから、アプリケーションを再度起動します。30 秒以内にアプリケーションを起動した場合、アプリケーションは分離ストレージからデータを取得する必要があります。

注注:

XNA フレームワーク アプリケーションの場合、[デバッグ時に非アクティブ化されたら廃棄状態にする] チェック ボックスは、[プロジェクトのプロパティ] ダイアログの [XNA Game Studio] タブにあります。

表示:
© 2015 Microsoft