« 【UWP アプリ開発】 今週の tweet [1/30~2/6] #uwpdev_jp | トップページ | 【#Xamarin.Forms】【#UWP】 「DEP0700 : アプリケーションの登録に失敗しました。エラー 0x80070057」 »

2017年2月 3日 (金)

【#Xamarin.Forms】【#UWP】 Windows アプリ認定キットの「アプリの事前起動」エラー

ストアにアップロードするパッケージを作ったとき、 続けてWindows アプリ認定キット (WACK) を実施すると、 「アプリの事前起動」の項目で不合格になることがあります。

これは Xamarin.Forms に限った話ではなく、 生粋の UWP アプリでも古いテンプレートで自動生成されたコードを使っていると出ます。

不合格が出たときのキャプチャを撮ってなかったので、 coelacanth さんのブログから拝借↓。

Windows アプリ認定キットの「アプリの事前起動」でエラー | 眠るシーラカンスと水底のプログラマー (2016/6/30)

WACK error

シーラカンスさんが記事に書いていますが、 このエラーが出たままでもストアの審査は通ります。 なのでほっといていいかというと、 そうでもなくて…

  • Windows 10 Build 10586 では、 システムに余計な負荷を掛けます (大したことはないはずですが)。
  • 10586 から導入された起動高速化技術 「プレランチ (事前起動)」 の恩恵が受けられません。

◆ UWP のプレランチ (事前起動) とは?

MSDN: アプリの事前起動の処理

システム リソースが許す限り、ユーザーの最も頻繁に使うアプリを事前にバックグラウンドで起動することで、デスクトップ デバイス ファミリのデバイスにおける Windows ストア アプリの起動時のパフォーマンスが向上します。

  • システムが事前に、 アプリ用のサンドボックスを構築し、 App クラスのインスタンスを作って OnLaunch まで一度走らせておく (画面は表示しない)。
  • これにより、 ユーザーがアプリを使うときにはすぐに画面が開く (起動時間が短縮される)。
  • この機能は PC のみ。 Mobile は対象外。 (上の文からは、 HoloLens も Surface Hub も Xbox も対象外に読めるけど…???)
  • 「ユーザーの最も頻繁に使うアプリ」 ということで、 Windows 側が何らかの計測データに基づいてプレランチ対象にするかどうかを決定するらしい。

◆ Windows 10 のビルドによるプレランチの挙動の違い

  • November Update (TH2) = version 1511 = build 10586
    すべての UWP アプリがプレランチ対象
  • Anniversary Update (RS1) = version 1607 = build 14393
    プレランチを「申請」したアプリだけが対象

14393 からは、アプリの実行中に次の API を呼び出してプレランチの「申請」をする必要があります。

CoreApplication.EnablePrelaunch(true)

 

◆ WACK で「アプリの事前起動」エラーが出るのは?

アプリが、 具体的には App.xaml.cs の OnLaunched メソッドが、 プレランチ対応になっていないからです。 生粋の UWP アプリで、 最近作ったプロジェクトなら大丈夫です。

10586 では無差別にプレランチ対象にします。 で、 システムがプレランチで App クラスの OnLaunch メソッドを呼び出してみると、 未対応のアプリは画面まで開こうとするので、 (ここは想像ですが) エラーとして処理し、 プレランチの対象から外すのでしょう。

ということで、 この WACK のエラーを無視しても、 実害はほとんどなさそうです。 ストアの申請も通りますし。
※ 私も最初は、 対処を考えている時間がなくて、 エラーを放置したままストアに出しました (汗;

◆ 「アプリの事前起動」エラーに対処するには?

App.xaml.cs の OnLaunch メソッドを、 最新の環境で作った生粋の UWP アプリのプロジェクトのものを真似て書き換えます。
Xamarin.Forms の場合は、 書き換えるのは UWP プロジェクトの方の App.xaml.cs です (PCL や Shared Project の App.xaml.cs じゃないですよ!)。

具体的には、 次のコードのように 3行追加するだけです (行末に「// ←」とコメントを付けた部分)。

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
  if (System.Diagnostics.Debugger.IsAttached)
  {
    this.DebugSettings.EnableFrameRateCounter = true;
  }
#endif
  Frame rootFrame = Window.Current.Content as Frame;

  // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、
  // ウィンドウがアクティブであることだけを確認してください
  if (rootFrame == null)
  {
    // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します
    rootFrame = new Frame();

    rootFrame.NavigationFailed += OnNavigationFailed;

    if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
    {
      //TODO: 以前中断したアプリケーションから状態を読み込みます
    }

    // フレームを現在のウィンドウに配置します
    Window.Current.Content = rootFrame;
  }
  // 【1】ここまではプレランチ時に実行される (本番起動時にも実行される)

  if (e.PrelaunchActivated == false) // ←
  { // ←
    // プレランチではないとき (=本番起動時) だけ、画面を開く

    if (rootFrame.Content == null)
    {
      // ナビゲーション スタックが復元されない場合は、最初のページに移動します。
      // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを
      //構成します
      rootFrame.Navigate(typeof(MainPage), e.Arguments);
    }
    // 現在のウィンドウがアクティブであることを確認します
    Window.Current.Activate();
  } // ←
}

※ MSDN 「アプリの事前起動の処理」 に載っているサンプルコードは間違っています。 その通りにしても WACK のエラーは直りませんでした。

※ 上のコードのコメント【1】のところまでは、 プレランチ時と本番起動時の2回とも実行されます。
1回しか必要のない初期化処理などを追加しているときは、 重複して実行されないように修正します。 (Xamarin.Forms の初期化処理「Xamarin.Forms.Forms.Init(e);」は、 「if (rootFrame == null)」の中に入っているので、 そのままで OK です。 )

◆ 「アプリの事前起動」を有効にするには?

14393 では、 既定ではプレランチが無効になっています。
アプリの実行中に、 適当なところで次の API を呼び出してプレランチの「申請」をする必要があります (「申請」すると、 その次の起動時からプレランチされる)。

CoreApplication.EnablePrelaunch(true)

ドキュメントに、 このメソッドは何回呼び出しても害はないとあるので、 OnLaunch メソッドの最後あたりに書いておけばよいでしょう。

if (e.PrelaunchActivated == false)
{
  if (rootFrame.Content == null)
  {
    rootFrame.Navigate(typeof(MainPage), e.Arguments);
  }
  Window.Current.Activate();

  // プレランチを「申請」します
  CoreApplication.EnablePrelaunch(true); // ←
}

ただし、 この CoreApplication.EnablePrelaunch メソッドは 14393 からです。
このまま 10586 で実行すると、 いきなり例外で落ちます! orz

ので、 10586 もターゲットに含めるときは、 次のように存在チェックが必要です。

if (e.PrelaunchActivated == false)
{
  if (rootFrame.Content == null)
  {
    rootFrame.Navigate(typeof(MainPage), e.Arguments);
  }
  Window.Current.Activate();

  // プレランチを「申請」します
  if (Windows.Foundation.Metadata.ApiInformation // ←
      .IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", // ←
                       "EnablePrelaunch")) // ←
      CoreApplication.EnablePrelaunch(true);
}

◆ 「アプリの事前起動」をデバッグするには?

Visual Studio 2015 Update 3 では、 メニュー [デバッグ] - [その他のデバッグ ターゲット] - [ユニバーサル Windows アプリ事前起動のデバッグ] を選びます。 すると e.PrelaunchActivated フラグが true の状態で OnLaunched が呼び出されるので、 事前起動をデバッグできます。

なお、 そのまま通常起動のデバッグに進むには、 スタート画面でタイルをタップすればよいみたいです。

20170203_xamarinforms03a

|

« 【UWP アプリ開発】 今週の tweet [1/30~2/6] #uwpdev_jp | トップページ | 【#Xamarin.Forms】【#UWP】 「DEP0700 : アプリケーションの登録に失敗しました。エラー 0x80070057」 »

* プログラミング ( Metro スタイル )」カテゴリの記事

* プログラミング ( Xamarin )」カテゴリの記事

プログラミング」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/209349/64846108

この記事へのトラックバック一覧です: 【#Xamarin.Forms】【#UWP】 Windows アプリ認定キットの「アプリの事前起動」エラー:

« 【UWP アプリ開発】 今週の tweet [1/30~2/6] #uwpdev_jp | トップページ | 【#Xamarin.Forms】【#UWP】 「DEP0700 : アプリケーションの登録に失敗しました。エラー 0x80070057」 »