最新のアプリ

Windows ストア アプリのライフサイクル

Rachel Appel

 

Rachel AppelWindows 8 では、アプリの実行方法と実行タイミングが変わります。常に適切に反応するアプリを作成できるように、アプリの新しいライフサイクルの微妙な違いを理解してください。マイクロソフトのライフサイクル管理のガイドラインに従ってアプリを作成すると、特に小さなデバイスではメモリやバッテリが確保されるため、優れたユーザー エクスペリエンスが実現されます。

アプリの設計

Windows ストア アプリには、設計について 2 つの重要な考え方があります。1 つは、アプリが全画面モード、スナップ モード、またはフィル モードで実行されること、もう 1 つは、ユーザーがあまり気を散らすことなく目の前のコンテンツに集中できるように高い応答性が求められることです。これらの2 つの原則から、現在実行中のアプリは OS およびユーザーの使用可能なリソースをすべて利用できるようになります。Windows 8 のデスクトップ アプリや以前のバージョンの Windows のアプリは、こうしたリソースを共有しなければなりませんでした。

すべての Windows ストア アプリには、非実行、実行、中断、および終了の 4 つの状態があります。アプリを起動すると実行状態になります。その後、ユーザーの使用状況またはシステムの稼働状況に応じて、実行状態と中断状態を繰り返します。たとえば、ユーザーがアプリ A からアプリ B に切り替えると、アプリ A は少し遅れて中断し、バックグラウンドに移動します。アプリ A は (ユーザーが再びアプリ A に切り替えるか、Windows がアプリ A を終了するまで) 中断状態のままになります。一方、アプリ B はアクティブになり、実行状態に移行します (アプリ B が既にメモリ内に存在すれば、中断状態から移行されることになります)。ユーザーが再びアプリ A に切り替えると、Windows はアプリ A を単純にアクティブ状態に戻し、OS とアプリ A の両方が認識している限りずっと実行中の状態になります。この場合、当然、アプリ B は中断状態になります。

アプリが中断状態になると、コードが実行されない状態でそのままメモリ内に保持されます。基本的に、アプリはキャッシュされ、ユーザーによって再度切り替えられるとすぐに実行できる状態です。ただし、これだけで終わる単純な話ではありません。正しい手順に従えばバックグラウンド タスクを実行することもできます。また、メモリの負荷が高くなると、Windows によってアプリが強制終了させられることもあります。図 1 に、アプリの実行状態の遷移について示します (詳細については、blogs.msdn.com/b/windowsappdev_ja/archive/2012/04/16/managing-app-lifecycle.aspx を参照してください)。

How Windows Store Apps Move Between Execution States
図 1 Windows ストア アプリの実行状態の遷移

図 1 からわかるように、アプリは実行状態と中断状態の間を頻繁に遷移する可能性があります。アプリ ライフサイクルの状態遷移は、Windows ストア アプリと従来のデスクトップ アプリケーションでは大きく異なります。

Visual Studio 2012 には、豊富なデバッグ ツールや Windows Simulator (bit.ly/QzBy5I、英語) があり、これらを使用してアプリ ライフサイクルの動作を管理できます。ライフサイクルの動作は、アプリ ライフサイクル イベントに適切に応答し、状態遷移を正しく処理するために重要です。これにより、Windows 8 でのアプリ設計の重要な原則であるエンド ユーザーに対する高い応答性を実現できます。アプリ ライフサイクル イベントに適切に応答することで、アプリ ライフサイクル全体を通じて一貫したユーザー エクスペリエンスを確保できます。具体的には、必要に応じてアプリの状態を保存および復元します。これにより、ユーザーをアプリの元の場所 (ウィザード内など) に戻したり、入力途中のフォームに値を再設定したり、直前の記事に戻したりすることができます。アプリのライフサイクルは、アプリを使用中のユーザーの動向によって決まるため、中断イベントが発生したらすぐにアプリの状態を確認する準備をしておく必要があります。

アプリのアクティブ化

WWAHost.exe プロセスは、Windows ストア JavaScript アプリを実行するアプリ ホストです。C#、Visual Basic、C++ などの言語で作成される XAML アプリは、対応するアプリの実行可能ファイルを実行します。いずれにしても、すべてのアプリは、次のようなさまざまなきっかけでアクティブ化が行われます。

  • ユーザーがタイルからアプリを起動する。
  • ユーザーが中断状態のアプリに切り替える。
  • 検索コントラクトまたは共有ターゲット コントラクトを通じて Windows によってアプリが起動される。
  • プロトコル (URI スキーム) の関連付けが呼び出されるか (msdn.microsoft.com/ja-jp/library/windows/apps/hh452686.aspx)、ファイルの関連付けによって、Windows からアプリが起動される。
  • ファイル オープン ピッカー コントラクトや連絡先ピッカーなどの拡張機能が Windows によって呼び出される。

アクティブ化が行われる方法によって、実行する必要のあるコードが決まります。タイル、コントラクト、またはプロトコルから起動されると、JavaScript 用 Windows ライブラリ (WinJS) アプリでは activated イベントが発生し、XAML アプリでは OnLaunched イベントが発生します。これらのイベントの発生時は、アプリの以前の状態を確認して、適切な動作を行います。

アプリが非実行状態から実行状態に遷移する場合、アプリはタイル、チャーム、プロトコル、または拡張機能から起動されたことがわかるため、新しくデータを読み込む必要があります。アプリが中断状態から戻る場合、一般には何もする必要ありません。ユーザーは中断した場所に戻るだけです。アプリが終了状態から実行状態に遷移する場合、データを読み込み直して、ユーザーが最後に使用した場所に移動する必要があります (ただし、ユーザーが最後に使用してから何か月も経過していない場合に限ります)。アクティブ化を処理するコードを図 2 (JavaScript) と図 3 (C#) に示します。コードからわかるように、Windows API には両方の言語に対応した便利な列挙型のセットが用意されているため、前の実行状態とアプリの起動方法をテストすることができます。この情報を取得したら、必要に応じてデータを再設定できます。

JavaScript の activated イベントと XAML の OnLaunched イベントには、アプリのアクティブ化の前の状態を特定するために参照できる args 引数があります。図 2図 3 は、アクティブ化イベントの例を示しています。

図 2 JavaScript アプリのアクティブ化コード

var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
app.addEventListener("activated", function (args) {
  if (args.detail.kind === activation.ActivationKind.launch) {
    if (args.detail.previousExecutionState !==
      activation.ApplicationExecutionState.terminated) {
      // TODO: This application has been newly launched.
      // Initialize your application here.
    } else {
      // TODO: This application has been reactivated from suspension.
      // Restore application state here.
    }
    if (app.sessionState.history) {
        nav.history = app.sessionState.history;
    }
    args.setPromise(WinJS.UI.processAll().then(function () {
      if (nav.location) {
          nav.history.current.initialPlaceholder = true;
          return nav.navigate(nav.location, nav.state);
      } else {
        return nav.navigate(Application.navigator.home);
      }
    }));
  }
});

図 3 C# アプリのアクティブ化コード

async protected override void OnLaunched(LaunchActivatedEventArgs args)
  {
// Check whether the session data should be restored.
    if (args.PreviousExecutionState == 
      ApplicationExecutionState.Terminated)
    {
      // Here we've created a SuspensionManager class that
      // handles restoring session data from a file and
      // then gives access to that data through a Dictionary.
      await SuspensionManager.RestoreAsync();
// Retrieve the data for restore.                 
data = SuspensionManager.SessionState["savedData"];
    }
    // If not, use the app's default values
    else
            {
data = "Welcome";
    }
    Window.Current.Activate();
}

アクティブ化時に実行するコードは 15 秒以内に完了する必要があります。さもないと、Windows によって強制終了させられます。この条件は厳しく感じるかもしれませんが、これ以上ユーザーが UI を操作できない状態が続くと、そのアプリは応答性が高いと言えなくなります。ユーザーと応答時間に関する UseIt.com の調査によると (bit.ly/NWSumy、英語)、多くのユーザーは読み込みに 10 秒以上かかる Web サイトの使用を途中で止めてしまいます。実際、ユーザーはページの読み込みに 1 ~ 2 秒以上かかると不満を感じます。また、多くのユーザーが、10 秒という基準の秒数が経過する前にサイトの使用をあきらめます。このようなユーザーに関する情報を把握しておくことは、Windows ストアでアプリの販売を計画する場合特に重要です。不満を感じたユーザーはアプリに否定的なレビューを投稿するようになります。十分機能しないアプリがストアの認定を受けられないのは言うまでもありません (アプリの提出の詳細については、msdn.microsoft.com/ja-jp/library/windows/apps/br230835.aspx を参照してください)。読み込むデータと読み込むタイミングを慎重に検討する必要があります。さいわい、Windows ランタイム (WinRT) ライブラリの優れた非同期プログラミング モデル (blogs.msdn.com/b/windowsappdev_ja/archive/2012/03/26/windows.aspx) を使用して、簡単にデータを高速かつ段階的に読み込むことができます。

Windows.ApplicationModel.Activation オブジェクトは、言語に関係なくすべての Windows ストア アプリでアクセス可能な Windows API の一部です。Activation オブジェクトには次の値を持つ activationKind 列挙型が含まれています。

  • Launch: ユーザーがアプリを起動したか、セカンダリ タイルをタップした。
  • Search: ユーザーがアプリを使用した検索を希望している。
  • ShareTarget: アプリが共有操作の対象としてアクティブ化されている。
  • File: アプリが処理ツールとして登録された種類のファイルが起動された。
  • Protocol: アプリが処理ツールとして登録されたプロトコルの URL が呼び出された。
  • FileOpenPicker: ユーザーがアプリによって指定されたファイルまたはフォルダーの選択を希望している。
  • FileSavePicker: ユーザーがファイルの保存を希望し、アプリがその保存場所として選択された。
  • CachedFileUpdater: ユーザーがアプリによってコンテンツ管理が行われているファイルの保存を希望している。
  • ContactPicker: ユーザーが連絡先の選択を希望している。
  • Device: アプリが自動再生を処理する。
  • PrintTaskSettings: アプリが印刷タスクを処理する。
  • CameraSettings: アプリが備え付けのカメラで写真やビデオを撮影する。

アプリが複数の機能 (共有と検索など) をサポートする可能性があるため、activationKind 列挙型を使用して、アプリ起動時のユーザーの目的を確認し、対応するコードを実行します。

中断、終了、および再開の管理

ユーザーが別のアプリに切り替えるか、デバイスが休止状態またはスリープ モードになると、Windows によってアプリ コードの実行が停止されますが、アプリはメモリ内に保持されます。このような中断を行うのは、ユーザーがアクティブに値を取得しているアプリに対するバッテリやパフォーマンスの影響を最小限に抑えるためです。中断状態または短時間の終了状態のアプリが Windows によってアクティブ状態に戻されると、ユーザーはアプリがずっと実行していたかのように感じます。ライフサイクルのイベントを適切に処理すると、応答性の高い設計を実現できます。

Windows は、アプリを中断状態にする前にデータやユーザー情報を保存できるところで、WinJS の oncheckpoint イベントまたは XAML の OnSuspending イベントを発生させて中断処理を開始します。これらのイベントで実行するすべてのコードは、10 秒間以内または Windows によってアプリが完全に終了するまでに完了することが求められます。アクティブ化の処理と同様、この規則によって OS の安定した正常性が全体的に保たれ、すばやい切り替えが可能になります。oncheckpoint イベントをフックするには、簡単な関数定義が必要です。

app.oncheckpoint = function (args) {
  // Save app data in case of termination.
};

XAML では、Application.Suspending イベントを使用します。

async void Suspending(Object sender,
  Windows.ApplicationModel.SuspendingEventArgs e) {
  // Save app data in case of termination.
}

中断処理中に、アプリ内でユーザーが操作している場所やスクロール位置を保存し、ファイル ハンドルと接続を解放して、データに日付スタンプやタイムスタンプを付ける必要があります。これは、組み込みの WinJS.Application.sessionState オブジェクトまたは XAML の SuspensionManager オブジェクトを使用して実現できます。Windows はアプリの終了前に、アプリの終了を通知できないため、常に、suspending イベントでユーザー情報とアプリ データを保存します。終了処理はさまざまな状況 (Windows でメモリを解放する必要がある、デバイスの (バッテリ) 電源が足りなくなるなど) で発生する可能性があるため、このことが重要になります。

そのため、コードは警戒心を持ちながら、アプリが終了することを想定して作成します。そのためには、アプリ データを毎回 sessionState に保存します。Windows によってアプリが終了する場合、データを保存して再設定に備えます。

再開処理は、Windows によってアプリが中断状態からアクティブ状態になるときに行われます。ほとんどの場合、ユーザーがアプリに再度切り替えるか再起動するときに Windows によってアプリが再開されるだけなので、コードに関して必要な作業はありません。中断中は、アプリはそのままの状態でメモリ内に保持されているだけです。したがって、アプリのコンテンツは変更されないままになっており、何か処理を実行する必要はありません。

ほとんどのアプリでは、中断状態から再開するにあたって必要な作業はありませんが、変更頻度の高いデータが格納されているアプリ (RSS フィード、株価情報、ソーシャル ネットワーキングなど) では、resuming イベントに対応することになります。resuming イベントは、こうした特定の数シナリオにしか使用されないことから Windows.UI.WebUI.WebUIApplication オブジェクトに格納されており、oncheckpoint、onactivated、およびその他兄弟関係にあるライフサイクル イベントが存在する一般的な WinJS.Application オブジェクトには含まれません。

Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function () {
  // Repopulate data from sessionState.
});

コードを整理するため、再開ハンドラーは、default.js ファイルのアクティベーション コードの近くに追加します。再開処理中は、最小限のデータをすばやく読み込むことでアプリの応答性を維持します。

バックグラウンド タスクとリアルタイム コミュニケーション アプリ

中断状態のアプリの UI コードが実行されることはありませんが、バックグラウンド タスク (https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/hh452975.aspx) の実行、大きなファイルの転送、または電子メールの確認を行うことは可能です。一部のリアルタイム コミュニケーション アプリ (IM クライアントなど) では、アプリの実行状態にかかわらず、メッセージ受信時にユーザーに通知できる必要があります。

バックグラウンド タスクは、アプリが技術的にはフォアグラウンドで実行中でないときに、特定の制約の下で定期的に実行される軽量のクラスです。こうしたタスクは、アプリが実行されていない状態でも、タスク独自のサンドボックスまたはアプリのコンテナー内で実行されます。JavaScript のバックグラウンド タスクは WWAHost.exe の新しいシングルスレッド アパートメントで実行され、JavaScript 以外のバックグラウンド タスクは、アプリ内のスレッド アパートメントに読み込まれたインプロセス .dll 内で実行されます。この区別を行うことで、ユーザーがアプリに戻るまで中断状態になっているアプリの UI とは関係なくバックグラウンド タスクを実行できます。

コードをバックグラウンドで実行できるだけでなく、これらのアプリではロック画面に情報を表示することも可能です。ロック画面には、時刻、日付、バッテリ状態、ネットワーク状態など、少量の情報を重ね合わせたバックグラウンド イメージが表示されます。また、アプリ (具体的にはバックグラウンドで実行しているアプリ) からロック画面に情報を表示することもできます。ロック画面を処理しやすい状態に保つことが重要なため、表示できる情報は限られています。ロック画面には、最大 7 つのアプリのバッジと 1 つのアプリ タイルを表示できます。詳細については、「ロック画面の概要」(https://msdn.microsoft.com/ja-jp/library/windows/apps/hh779720.aspx) を参照してください。

モデルに従う

Windows ストア アプリの開発者としては、バッテリ残量、メモリ負荷、さまざまなデバイスでのアプリの実行、(そして最も重要な) UX といった問題を考慮する必要があります。所定のライフサイクル管理ガイドラインに従えば、目標は達成しやすくなります。また、アプリのライフサイクル管理に関する技術的な理由以外でも、アプリで高い応答性やパフォーマンスを実現できれば、Windows ストアで評価の高いレビュー結果を得ることができます。

Rachel Appel は、ニューヨーク市でマイクロソフトの開発者エバンジェリストを務めています。彼女に連絡を取るには、Web サイト (rachelappel.com) にアクセスするか、rachel.appel@microsoft.com. 宛てにメールを送ってください。また、近況については Twitter (twitter.com/rachelappel) をフォローしてください。

この記事のレビューに協力してくれた技術スタッフの Adam Barrus、Ben Betz、Arun Kishan、Michael Krause、Hari Pulapaka、John Sheehan、および Ben Srour に心より感謝いたします。