Share via


モバイルの問題

Windows Phone 7 のトゥームストーン処理

Jaime Rodriguez

コード サンプルのダウンロード

優れたモバイル プラットフォームは、デバイスを携行することによって課せられるハードウェアの制約を認識する必要があります。デスクトップ コンピューターと比べると、モバイル デバイスには、メモリ、処理能力、画面スペース、バッテリ寿命に制限があります。こうした制約が積み重なると、多くのアプリケーションが実行される汎用デバイスでは、他のアプリケーションのリソースを確保するためにアプリケーションが終了したりシャットダウンされたりします。

Windows Phone は、トゥームストーンという機能によって、こうした制約に対処します。トゥームストーンはわかりやすい提案のように思えますが、実は、開発者の間で論争を引き起こしています。必要ないという開発者もいれば、難しすぎるという開発者もいます。単に、機能名が好みではないという開発者もいます。とにかく言えるのは、モバイル デバイスの制約によって、トゥームストーンは避けられない問題となるため、優れたモバイル アプリケーションはトゥームストーンに対処しなければなりません。

Windows Phone アプリケーションのライフサイクル

Windows Phone 開発に初めて取り組む開発者の多くは、このプラットフォームを使用するとき、アプリケーションのライフサイクルを次のように想定します。

  1. 開始
  2. 実行
  3. 終了
  4. 1 に戻り、再開

このような想定に反して、Windows Phone 7 は、プロセス指向は低く、セッション指向が高いライフサイクルになります。

Windows Phone 7 では、ライフサイクルを次のように考えます。

  1. 開始
  2. 実行
  3. 実行の中断または終了
  4. 中断から復帰、または中断後新たに再開
  5. 終了後新たに再開

この新しいセッション指向のモデルのメリットは、OS でのリソース管理方法をユーザーが意識する必要なく、アプリケーションを切り替えることが可能になる点です。ユーザーは、SMS の受信メッセージに返信するためゲームを中断し、その結果ゲームのプロセスが強制終了されても気に留めません。メッセージを送信したらゲームに戻るつもりです。問題なくそれが行われれば、基になる詳細は関係ありません。

開発者にとってのデメリットは、セッションがプロセス中心の従来の OS で実行されるため、セッションが継続していると感じられるようになんらかの対処が必要な点です。プロセス中心の環境にセッションを適応させるには、Launched (起動)、Activated (アクティブ)、Running (実行)、Deactivated (非アクティブ)、Tombstoned (トゥームストーン)、および Closed/Ended (終了) というセッションの論理状態を作成します。

図 1 に、Windows Phone 7 アプリケーションの実際のライフサイクルを示します。図 2 の表に示すアプリケーション ライフサイクル イベントは、Microsoft.Phone.Shell.PhoneApplicationService クラスによって公開されます。

Windows Phone 7 Application Lifecycle

図 1 Windows Phone 7 アプリケーション ライフサイクル

図 2 アプリケーション ライフサイクルのイベント

論理状態 PhoneApplicationService イベント 説明
Launched (起動) Launching アプリケーションは、ユーザーがスタート タイルまたはアプリケーション リスト アイコンを押すか、トースト通知をクリックすると起動します。"起動" とは、セッションが新しく開始されることです。
Activated (アクティブ) Activated アプリケーションは、ユーザーが "戻る" ボタンを押して、以前に非アクティブにしたアプリケーションをフォアグラウンドに戻すとアクティブになります。この場合、ユーザーはそれまで実行していたセッションに戻ることを期待します。
Running (実行) Running 起動またはアクティブになった後、アプリケーションが実行されます。
Deactivated (非アクティブ) Deactivated 実行中のアプリケーションは、フォアグラウンドのプロセスが、別のアプリケーションまたは OS コンポーネント (セレクター、ランチャー、ロック画面など) に移行すると非アクティブになります。セッションが中断されても、後に再開すると想定されます。
Ended/Exited (終了) Closing

ユーザーは、メイン ページで "戻る" キーを押すことで、アプリケーションを終了します。

ユーザーは、アプリケーションを終了したら、新たに再起動すると想定します。

トゥームストーン状態は、やや複雑で、PhoneApplicationService イベントとは直接関係していません。アプリケーションが非アクティブになると、OS はそのアプリケーションのプロセスを即座には強制終了しません。理論上は、OS がリソースを必要とするときに、そのアプリケーションを強制終了します。アプリケーションはまったく通知を受けず、単に強制終了されます。

実際には、Windows Phone 7 は、別のフォアグラウンド アプリケーションに制御が移行した直後にプロセスを強制終了しますが、この終了タイミングを前提とすべきではありません。マイクロソフトは、2 月の Mobile World Congress で、既に Fast App Switching などの拡張機能を搭載する予定であることを発表しているため、トゥームストーン状態になるタイミングを判断するのに、実装の詳細部分に依存しないようにしてください。代わりに、非アクティブ状態を適切に操作して、強制終了の準備を行います。

非アクティブとトゥームストーン

携帯電話では、多くのプロセス (シェル、電話機能など) が常時実行されていますが、フォアグラウンドで実行されるアプリケーションは最大 1 つです (フォアグラウンドで何も実行されていなければ 0 です)。

フォアグラウンド アプリケーションは、別のアプリケーションまたは OS コンポーネントに制御が移ると非アクティブになります。プロセスが非アクティブになると、OS はリソースを解放するためにそのプロセスを強制終了します。この状態をトゥームストーンといいます。

おわかりのように、トゥームストーンは、アプリケーションが非アクティブになると必ず行われるわけではありませんが、トゥームストーン状態になるのは常に非アクティブになった後です。実際、Deactivated はトゥームストーン状態になる前に PhoneApplicationService が発生する最後のイベントなので、アプリケーションを再度アクティブにする作業はこの状態から実行する必要があります。

図 3 では、非アクティブ状態を引き起こす操作をすべて示し、トゥームストーン状態に移行する可能性について推測しています。

図 3 非アクティブになる作業

操作 非アクティブ状態なるかどうか トゥームストーン状態になるかどうか
ユーザーがアプリケーションの先頭ページで "戻る" ボタンを押す 非アクティブにはならず、アプリケーションが終了する 非アクティブには決してならないため、トゥームストーン状態にはならない
ユーザーが "スタート" ボタンを押す 非アクティブになる 可能性は高いが、トゥームストーン状態になるとは限らない。アプリケーションが非アクティブになった数秒後にトゥームストーン状態になります。非アクティブになった直後にユーザーがアプリケーションに戻せば、トゥームストーン状態にはなりません。これは、不確定なタイムアウトと考えられます。
ユーザーが、トゥームストーン状態に移行するセレクターかランチャーを呼び出す 非アクティブになる 可能性は高いが、タイムアウトが適用される
ユーザーが、トゥームストーン状態に移行しないセレクターまたはランチャーを呼び出す 非アクティブになる あまりないが、トゥームストーン状態に移行することもある。ユーザーがタスクの最中に "スタート" ボタンを押すと、上記の「ユーザーが "スタート" ボタンを押す」ときの規則が適用されます。アプリケーションが既に非アクティブになっているため、新たに Deactivated イベントは発生しません。
ロック画面が表示され、ロックされた状態でも実行するようにアプリケーションが構成されていない 非アクティブになる 可能性は高いが、タイムアウトが適用される
トースト通知が表示され、ユーザーがそれをタップし、別のフォアグラウンド アプリケーションに移動する 非アクティブになる 可能性は高いが、タイムアウトが適用される

即座にトゥームストーン状態に移行しないセレクターのサブセットもありますが、プロセスがトゥームストーン状態になる操作をユーザーが行うとトゥームストーン状態になります。このサブセットには、PhotoChooserTask (ユーザーがトリミングを指定しない場合)、CameraCaptureTask、MediaPlayerLauncher、EmailAddressChooserTask、および PhoneNumberChooserTask があります。

その他のセレクターとランチャーはすべて、Show メソッドが呼び出された直後にトゥームストーン状態に移行します。

Windows Phone 7 アプリケーション ライフサイクルの動作を確認するには、コード ダウンロードの LWP.TombStoning サンプルを実行してください。

状態の保存と復元

セッション ベースのナビゲーションの目的は、ユーザーがフォアグラウンド アプリケーションをシームレスに切り替えるのを容易にすることなので、関連するすべての状態を Deactivated イベントで保存し、Activated イベントで復元する必要があります。大半のアプリケーションでは、次の 3 種類の状態を管理します。

  • 固定的なアプリケーション状態: 常に保存する必要があります。たとえば、アプリケーションの設定やユーザー データなどです。
  • セッション固有のアプリケーション状態: アクティブになるときには復元する必要があっても、アプリケーションを新たに起動するときは新しい状態で開始する必要がある、キャッシュやビューモデルなどの一時状態です。
  • UI またはページ固有の状態: アプリケーションがアクティブになるときに PhoneApplicationPage を復元するために必要な状態です。Windows Phone 7 は、トゥームストーン状態に移行するときに、アプリケーションの戻るスタックを保存します。アプリケーションをアクティブにするとき、Windows Phone 7 は、そのアプリケーションがトゥームストーン状態に移行する直前にアクティブだったページのみを復元します。ユーザーが "戻る" ボタンを押すと、前のページのインスタンスが作成されます。

固定的なアプリケーション状態は、分離ストレージに保存するか、ApplicationSettings クラスを使用して保存します。このアプリケーション状態は、バッテリー切れなどを想定して、できるだけ早い段階で保存します。

(セッション キャッシュなどの) ユーザー データで、あまり頻繁にシリアル化することを望まない場合は、Deactivated イベントと Closing イベントの両方で保存し、(それぞれ対応する) Activated イベントまたは Launching イベントで復元します。

セッション固有の状態は、シリアル化形式を制御する場合や、データ量が多い場合は、分離ストレージに保存できます。また、PhoneApplicationService.State ディクショナリに保存することも可能です。セッション固有の状態は、Deactivated イベントでのみ保存し、Activated イベントで復元します。

ページ固有の状態は、PhoneApplicationPage.State ディクショナリに保存します。ページ状態を保存する鍵は、PhoneApplicationService.Deactivated イベントが発生するときに、アプリケーションにはページの戻るスタックが存在し、自動的にシリアル化されることを覚えておくことです。トゥームストーン状態への移行に備えてページを準備するには、ページで PhoneApplicationPage.OnNavigatedFrom オーバーライドをリッスンして、ページのディクショナリに、モデル (またはビューモデル) にまだコミットされていないビュー ステートを保存します。戻るスタックのページに移動できなくなるため、Deactivated イベントが発生するまで待機しないでください。

もちろん、OnNavigatedFrom を受け取ったときページ状態を保存する場合は、ページの OnNavigatedTo オーバーライドで復元します。

また、ビューモデルにページ固有の状態を保存して、ビューモデルをセッション状態としてシリアル化することもできますが、このためにはコミットされていない状態をビューモデルに保存する必要が生じるためお勧めできません。適切なインフラストラクチャを活用して、プラットフォームを後から最適化するために、将来への耐性を維持しましょう。

落とし穴を避ける

トゥームストーンは難しくありません。少々の手間と、一貫性や計画が必要なだけです。アプリケーションが非アクティブになってもトゥームストーン状態に移行しなければ、状態はメモリ内にとどまり、再構築は行いません。

アプリケーションが必要とする状態の作成をクラスのコンストラクターに依存するのは避けます。ここで作成した状態は非アクティブに移行する過程で解放されることがあります。対称性を保つことをお勧めします。アプリケーション レベルの状態の保存と復元には PhoneApplicationService.Deactivated イベントと PhoneApplicationService.Activated イベントのペアを、ページ状態の保存と復元には OnNavigatedFrom または OnNavigatedTo を使用します。

(おそらく、インスタンスの作成が遅延されることが原因で) アクティブ化の呼び出し以外でインスタンスが作成されるオブジェクト (シングルトン) がアプリケーションに存在する場合は、こうしたオブジェクトを使用する前に、正しく構築および初期化されていることを常にチェックします。よくある失敗は、PhoneApplicationService.Activated イベントまたは PhoneApplicationPage.OnNavigatedTo でデータを読み取って、それを再設定しないことです。ページは、(トゥームストーン状態に移行するかどうかに関わらず) 複数回 NavigatedTo 状態になる可能性があり、セッションも、セッション中に複数回トゥームストーン状態に移行する可能性があります。

状態を復元したら、保存した状態はクリアします。状態は、ページの場合は NavigatedFrom オーバーライド、アプリケーションの場合は Deactivated イベントで後から設定することができます。

保存の対象と復元のタイミングについて考慮します。ユーザーがアプリケーションにシームレスに戻れるようにするため必要なことの 1 つは、アプリケーションを即座に復元することです。ページ状態またはアプリケーション状態をあまりにも多く保存すると、アクティブになるのが遅くなります。アプリケーションがアクティブになるときにすぐには必要にはならない状態は、必要に応じて分離ストレージを使用して、状態のバックグラウンド読み込みを行います。アクティブ化と非アクティブ化は、10 秒以内に実行します。この点に注意しておかないと、非アクティブ状態への移行が完了する前、または再度アクティブになるときに OS がプロセスを強制終了する場合があります。ただし、10 秒よりもはるかに短くすることを目標にしてください。

シリアル化フレームワークの制約を把握します。すべてのページ状態とアプリケーション状態の中で保存できるのは最大約 2 MB です。総計がそれを超えると、アプリケーションを切り替えるときや、アプリケーションが非アクティブになるときに例外が発生することになります。ページ状態でこのように多くのデータをシリアル化しないようにします。大量のデータをキャッシュする必要がある場合は分離ストレージに保存します。

ページ ナビゲーションにはクエリ文字列を使用します。コンテキストを新しいページに渡す必要がある場合は、そのページに渡すクエリ文字列を使用してすべてのデータを渡すか、一意識別子 (トークン) をサービス ロケーターに渡します。サービス ロケーターはトークンに基づいてデータをフェッチできます。トゥームストーン状態になってから再びページがアクティブになるときに、ビューモデルまたはページ状態が使用可能であるとは仮定しません。

セレクターとランチャーについて理解します。必ずしもすべてがトゥームストーンに移行するわけではありません。また、セレクターのイベント リスナーを設定する方法については特別な規則があります。これらの規則については、「方法: Windows Phone のセレクターを使用する」(bit.ly/edEsGQ、英語) を参照してください。

OnNavigatedTo と PhoneApplicationPage.Loaded との関係に考慮します。OnNavigatedTo 内では、ページのビジュアル ツリーがまだ完全には構築されていません。復元状態を取り出さなければならないことはよくありますが、ページの Loaded イベントが発生するまで、UI の状態の復元を待機します。遅延させる必要がある操作には、ほかにも Focus の設定、ピボットやスクロールにおける SelectedIndex の設定などがあります。

データを保存および復元するのに多くの作業を行っている場合 (この状態は避けるべきです) は、高度な最適化をいくつか検討します。これは、必要なときにだけ行い、徹底的にテストします。

トゥームストーン状態を検出するには、Deactivated イベントでアプリケーション クラスにフラグを設定します。Activated イベントでフラグがリセットされていなければ、トゥームストーンには移行しておらず、すべてのページがメモリ内にあり、復元する必要はありません。複数のページでのトゥームストーンの検出とフラグの設定を組み合わせるためには、セッション内で毎回アクティブになるたびにトークンを使用することができます。

また、ページの OnNavigatingFrom オーバーライドをリッスンして移動方向を検出し、最適化を行うこともできます。ページを戻る方向に移動する場合、そのページを破棄することになるため、状態を保存する理由がありません。

繰り返しになりますが、正しくトゥームストーンを処理する鍵は計画です。アプリケーションの開発サイクルの最後までトゥームストーンの処理を後回しにするのではなく、開発サイクルに組み込むようにします。早目に計画を行い、計画に従って実装し、徹底的にテストを行います。

良質なアプリケーションを作るために

最後に紹介する開発者向けのヒントは、操作性をシームレスにすることに心を砕くことです。Windows Phone 7 のコントロールを使用すれば、ページを容易に移動することができます。ページやアプリケーションの切り替えを行っていないように思えるくらいにシームレスな操作性を実現するには、以下の情報を復元することを検討します。

  • ピボットの SelectedIndex (ページにピボットを含む場合)
  • ListBox 内のスクロール位置、またはページ内のその他の ScrollViewer 内のスクロール位置
  • マップ コントロール、操作をサポートする画像ビューアーなどのコントロールの拡大レベルやその他の変換、
  • TextBox 内のコミットされていないテキスト。TextBox がアプリケーションにとって重要な場合 (Twitter アプリケーションのつぶやきテキストなど)、テキストの選択状態、キャレットの位置、フォーカスの復元について検討します。
  • ページ内でフォーカスが設定されている要素 (特に、SIP を表示する必要がある TextBox の場合)

復元すべきでないのは、パノラマの SelectedItem です。パノラマはこれをサポートしておらず、パノラマの DefaultItem の設定は、適切なページへのパンとは異なります。トゥームストーン状態になる前に選択されていたパノラマ項目に戻る手段として DefaultItem を使用しないことをお勧めします。

これらのヒントの動作を実際に確認するには、コード ダウンロードの LPW.TombstoningWithState サンプルを実行してください。readme.txt ファイルには、各シナリオについての参照先とスクリプトを含めています。

終わりに

今回のコラムは、決してトゥームストーン処理に関する包括的なリファレンスではありません。ですが、何が必要かを理解して、自身の Windows Phone 7 アプリケーションへのトゥームストーン処理パターンの導入に着手できるようになったのではないでしょうか。ユーザー エクスペリエンスがどれだけ向上するか、すぐにおわかりいただけるはずです。

Jaime Rodriguez は、マイクロソフトのプリンシパル エバンジェリストとして、Silverlight、Windows Phone などの新たなクライアント テクノロジの導入に従事しています。Twitter (twitter.com/jaimerodriguez、英語) で彼をフォローできます。また、blogs.msdn.com/jaimer (英語) にブログを公開しています。

この記事のレビューに協力してくれた技術スタッフの Peter Torr に心より感謝いたします。