2011年12月13日火曜日

任意のタイミングでトゥームストーン状態にする。(メモリーを喰い尽くせ!)

Windows Phone Advent Calender 13日目です。

内容は

任意のタイミングでアプリをトゥームストーン状態にする!

です。


  • そもそもトゥームストーン状態とは?

アプリケーションが非アクティブ状態の時に、OSのリソースが不足すると、OSは非アクティブのアプリケーションのプロセスを強制終了してリソースを確保します。
この時の状態をトゥームストーン(Tombstoned)状態といいます。

このトゥームストーン状態になったアプリケーションがアクティブに戻るときはリソースが解放された状態から再開しなければならないため、事前に作業中の情報を保存したりしないといけません。

この辺りは、今回の内容とは少し外れるため、詳しくはこの辺りをじっくり読んでください。



  • デバッグ実行でトゥームストーンにする方法

さて、作ったアプリがトゥームストーン状態になって戻ってきたとき、ちゃんと情報が保存/復元できるかをテストしないといけないわけですが、一般的な方法としては、デバッグ時のオプションを利用する方法があります。

WindowsPhoneアプリのプロジェクトのプロパティを開くと、デバッグタブ内に「デバッグ中の非アクティブ化時に廃棄する」というチェックボックスがあります。


このチェックボックスにチェックを入れると、アプリケーションを非アクティブ化した時(例:アプリ動作中にスタートボタン押下)、毎回必ずトゥームストーン状態にしてくれます。
そのため、効率的にトゥームストーン状態からの復帰処理をデバッグすることができます。


  • ○○Taskだと、うまくいかない・・・

基本的には上記の方法でトゥームストーン状態のテストが可能ですが、それが出来ないパターンがあります。

例えば、CameraCaptureTaskをアプリ内から起動し、写真を撮影後、そのデータを取得しようとしますが、トゥームストーン状態からの復帰となるためか、うまくデータが取得できません・・・。

そのため、○○Taskを使ったアプリでトゥームストーン状態のテストをするのは困難なのです。


この件についてUX-TVでも質問してみたところ、大西さん曰く

「任意のタイミングでトゥームストーン状態にするのは難しい。」
「しばらく複数のアプリ立ち上げて待ってればなるかもしれないけど、非効率ですね。」

ということでした。


  • よろしい、ならば落としてやろう

そりゃ困ったなーということで、こっちの意図したタイミングで無理やりトゥームストーン状態に突き落す方法を考えました。

要は、OSのリソースを不足させてやればいいわけですよ。

OSのリソースって何よ?ってことですが、プロセスを殺す必要があるということは、きっとメモリーじゃないかと推測し、メモリーを可能な限り確保してリソースを喰い尽くすことにしました。

private void AllocMemory(int mib)
{
 int bytes = mib * 1024 * 1024;
 AllocatedMemories.Add(new byte[bytes]);
}

private void FullAlloc()
{
 for (; ; )
 {
  try
  {
   AllocMemory(2);
  }
  catch (OutOfMemoryException)
  {
   MessageBox.Show("メモリを食い尽くしました");
   break;
  }
 }
}


こんな感じで1MiBずつ確保する関数を作って、OOMが発生するまで2MiBずつ確保していきます。

そしてできたのが、MemoryEaterGreenです。

IS12Tの場合、デバイスメモリ362MBのうち、211MBを食い潰すことに成功しました。

この図はエミュレータの場合で、427MBのうち276MBを確保しました。


しかし、一つのアプリで確保できるメモリの制限に引っかかってしまい、非アクティブ状態のアプリをトゥームストーン状態に追いやるのに十分なメモリを確保できませんでした。

そこで、MemoryEaterRedの出番です。(Greenの色違い。機能的な差はなし)

MemoryEaterGreenでメモリを食い尽くした後、「スタートボタン」でホーム画面に戻り、
今度はMemoryEaterRedを起動し、同じく、メモリを食い尽くします。

そうすると、MemoryEaterGreenで食い尽くすときよりも、Redで食い尽くすときのほうが少し時間が掛かると思います。
このとき、裏で非アクティブ状態のアプリケーションがトゥームストーン状態になり、確保していたリソースが解放されています。

つまり、


  1. アプリを起動し、トゥームストーン状態テストしたい画面まで遷移させ、スタートボタンを押下(非アクティブ化)
  2. MemoryEaterGreenを起動し、[Allocate Full]を押下 → スタートボタンを押下(非アクティブ化)
  3. MemoryEaterRedを起動し、[Allocate Full]を押下 → 戻るボタン押下 x 3

これで、対象アプリがトゥームストーン状態から復帰するはずです。
対象アプリが復帰するとき、「再開中・・・」という画面になれば、バグ技の成功です!

もし、GreenRedでも足りないくらいメモリを積んだ端末が出たときには、BluePikachuを自分で作ってください。

  • プロジェクトファイル一式
プロジェクトファイル一式はこちらからどうぞ