中断からの再開時に処理をするには?[ユニバーサルWindowsアプリ開発]:WinRT/Metro TIPS
ユニバーサルWindowsアプリでユーザーデータを保存したり、リソースを解放/取得したりするために使える各種のイベントを紹介する。
powered by Insider.NET
Windowsランタイムアプリ*1では、中断からの再開時に何らかの処理が必要になることがある。例えば、中断時に開放したリソースを再取得するとか、長時間にわたって中断されていた場合にはコンテンツを読み込み直すといった処理である。そのような処理はどう書けばよいのだろうか? また、リソースの解放/再取得については、アプリが切り替えられるときに行わねばならない場合もある。本稿では、両方について解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #99」からダウンロードできる。
*1 このMSDNのWebページは、2014年12月下旬に改訂された。以前は「ストアアプリ」と記述されていた部分が「Windowsランタイムアプリ」に変更されている。
事前準備
ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Community 2013 with Update 4を使っている。
- SLAT対応のPC*2
- 2014年4月のアップデート*3適用済みの64bit版Windows 8.1 Pro版以上*4
- Visual Studio 2013 Update 2(またはそれ以降)*5を適用済みのVisual Studio 2013(以降、VS 2013)*6
*2 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*3 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。
*4 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。
*5 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 4のもの)。
*6 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2013 with Update 4 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsランタイムアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。また、昨年11月12日(米国時間)に新しくリリースされたVisual Studio Community 2013 with Update 4(製品版)もマイクロソフトのページから無償で入手できる。Communityエディションは本稿執筆時点では英語版だけなので、同じ場所にあるVisual Studio 2013 Language Packの日本語版を追加インストールし、オプションダイアログで言語を切り替える必要がある。
用語
本稿では、紛らわしくない限り次の略称を用いる。
- Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
- Phone:Windows Phone 8.1
サンプルコードについて
Visual Studio 2013 Update 2(Update 3/4も)では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*7。そのため、本稿で紹介するVBのコードはユニバーサルプロジェクトではなく、PCL(ポータブルクラスライブラリ)を使ったプロジェクトのものである*8。
*7 VB用のユニバーサルプロジェクトは、今年にリリースされるといわれているVisual Studio 2015(開発コード「Visual Studio 14」)からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、2014年6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。
*8 Visual Studio 2013 Update 2(またはそれ以降のUpdate)のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。PCLに置いたものは、コードだけでなくXAML(画面)やリソースディクショナリなども共通に利用できる。そこで別途公開のサンプルコードでも、VBはWindows用/Phone用/共通コード(PCL)の3プロジェクト構成とした。ユニバーサルプロジェクトで作らなくてもユニバーサルWindowsアプリはリリースできるのである(「WinRT/Metro TIPS:ユニバーサルプロジェクトで開発するには?」参照)。
中断/再開時に行う処理
アプリが中断/再開されるときに行うべき処理としては、次のようなものがある。
- 再開時にコンテンツを取得し直して、表示を更新する(長く中断されていた場合)
- 編集中のファイルを中断時にクローズし、再開時にオープンし直す
このような処理は、Applicationクラス(Windows.UI.Xaml名前空間)のSuspendingイベント(中断時)/Resumingイベント(再開時)で行えばよい(コンテンツを再取得する場合は、中断時に時刻を記憶しておき、再開時に一定時間以上経過していたら読み込み直すようにするとよい)。これは任意の画面やコントロールで処理できる。ただし、Appクラスでも中断/再開時の処理を行っている場合は、処理の競合に注意が必要だ*9。
例としてユーザーコントロールで処理をする場合は、次のようなコードになる。ここではサンプルということで、リソースの解放/再取得といった処理の代わりに、中断/再開時の時刻を画面へ表示する処理が記述してあると思ってほしい(実際のコードは省略)。
public sealed partial class MyUserControl : UserControl
{
public MyUserControl()
{
this.InitializeComponent();
// 中断されるときのイベント
Application.Current.Suspending += Application_Suspending;
// 中断から再開されたときのイベント
Application.Current.Resuming += Application_Resuming;
}
// 中断されるとき
private void Application_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
SetSuspendTime();
deferral.Complete();
}
// 中断から再開されたとき
private void Application_Resuming(object sender, object e)
{
SetResumeTime();
}
private void SetSuspendTime()
{
……省略(現在時刻を画面に表示する)……
}
private void SetResumeTime()
{
……省略(現在時刻を画面に表示する)……
}
}
Public NotInheritable Class MyUserControl
Inherits UserControl
Public Sub New()
InitializeComponent()
' 中断されるときのイベント
AddHandler Application.Current.Suspending, AddressOf Application_Suspending
' 中断から再開されたときのイベント
AddHandler Application.Current.Resuming, AddressOf Application_Resuming
End Sub
' 中断されるとき
Private Sub Application_Suspending(sender As Object, e As SuspendingEventArgs)
Dim deferral = e.SuspendingOperation.GetDeferral()
SetSuspendTime()
deferral.Complete()
End Sub
' 中断から再開されたとき
Private Sub Application_Resuming(sender As Object, e As Object)
SetResumeTime()
End Sub
Private Sub SetSuspendTime()
……省略(現在時刻を画面に表示する)……
End Sub
Private Sub SetResumeTime()
……省略(現在時刻を画面に表示する)……
End Sub
End Class
中断されるときの「e.SuspendingOperation.GetDeferral()」〜「deferral.Complete()」については、「WinRT/Metro TIPS:中断時にユーザーデータをファイルに保存するには?[ユニバーサルWindowsアプリ開発]」を参照してほしい。
省略した現在時刻を画面に表示する部分は、単に画面上のコントロールに現在時刻を設定しているだけである。別途公開のサンプルコードをご覧いただきたい。
*9 イベント処理は非同期処理の完了を待ってくれないので、一つのイベントに複数のイベントハンドラーを結び付けると複数の非同期処理が同時に走る可能性がある。スレッド間の排他処理が必要な場合は、「.NET TIPS:非同期:awaitを含むコードをロックするには?(AsyncLock編)[C#、VB]」を参照してほしい。
アプリの切り替え時に行う処理
アプリの切り替え時、すなわち、他のアプリやスタート画面に切り替えられたとき(=自アプリが非表示にされるとき)と他のアプリやスタート画面から自アプリに切り替えられたとき(=自アプリが表示されるとき)に行うべき処理がある。
例えば、カメラやマイクなどといったシステムに一つしかないリソースを使う場合だ。そのようなリソースは、非表示にされるときに他のアプリのために明け渡し、表示されるときに再び取得しなければならない。
このような処理は、Windowクラス(Windows.UI.Xaml名前空間)のVisibilityChangedイベントで行えばよい。任意の画面やコントロールで処理できる。
例としてユーザーコントロールで処理をする場合は、次のようなコードになる。ここではサンプルということで、前と同様に省略してあるが非表示/表示時の時刻を画面へ表示する処理が記述してあると思ってほしい。
public sealed partial class MyUserControl : UserControl
{
public MyUserControl()
{
this.InitializeComponent();
// ウィンドウの表示/非表示が切り替わったときのイベント
Window.Current.VisibilityChanged += Window_VisibilityChanged;
}
// ウィンドウの表示/非表示が切り替わったとき
private void Window_VisibilityChanged(object sender, Windows.UI.Core.VisibilityChangedEventArgs e)
{
if (!e.Visible)
{
// 非表示にされるとき
SetHideTime();
e.Handled = true;
}
else
{
// 表示されるとき
SetShowTime();
e.Handled = true;
}
}
private void SetHideTime()
{
……省略(現在時刻を画面に表示する)……
}
private void SetShowTime()
{
……省略(現在時刻を画面に表示する)……
}
}
Public NotInheritable Class MyUserControl
Inherits UserControl
Public Sub New()
InitializeComponent()
' ウィンドウの表示/非表示が切り替わったときのイベント
AddHandler Window.Current.VisibilityChanged, AddressOf Window_VisibilityChanged
End Sub
' ウィンドウの表示/非表示が切り替わったとき
Private Sub Window_VisibilityChanged(sender As Object, e As Windows.UI.Core.VisibilityChangedEventArgs)
If (Not e.Visible) Then
' 非表示にされるとき
SetHideTime()
e.Handled = True
Else
' 表示されるとき
SetShowTime()
e.Handled = True
End If
End Sub
Private Sub SetHideTime()
……省略(現在時刻を画面に表示する)……
End Sub
Private Sub SetShowTime()
……省略(現在時刻を画面に表示する)……
End Sub
End Class
このコードにはないが、非表示になるときの処理時間が長い(100ミリ秒の単位で測れる長さの)ときは、次に表示されるときの処理との間で排他処理を行うべきだ。エンドユーザーがアプリを素早く切り替えたとき、それらの処理が同時に実行されてしまう可能性が出てくるからだ。
省略した現在時刻を画面に表示する部分は、単に画面上のコントロールに現在時刻を設定しているだけである。別途公開のサンプルコードをご覧いただきたい。また、別途公開のサンプルコードには、MediaCaptureクラス(Windows.Media.Capture名前空間)を使ってカメラのプレビューを表示する処理も含まれているので、参考にしていただきたい。
実行結果
以上の二つのコードをともに実装し、実行してみると以下のような結果になる(次からの画像)。これには、説明していないカメラのプレビューを表示する処理も入っている(別途公開のサンプルコードには含まれている)。それぞれの処理を行った時刻の表示に着目してほしい。
アプリを起動したとき(Windows Phone 8.1)
別途公開のサンプルコードはユニバーサルWindowsアプリである。Windowsでも動作するが、ここではPhoneの画像を示す。
アプリを起動したときは、表示のイベントだけが発生する。カメラを使う場合には、このタイミングでリソースの取得を行えばよい。
アプリを切り替えたとき(Windows Phone 8.1)
別途公開のサンプルコードはユニバーサルWindowsアプリである。Windowsでも動作するが、ここではPhoneの画像を示す。これはVS 2013付属のエミュレーターでデバッグ実行している画面である。
他のアプリに切り替えてまたすぐに戻したとき、中断状態にならなかった場合でもこのように非表示/表示のイベントが発生する(中断されるまでに、Windowsでは数秒程度以上の余裕がある。Phoneでは直ちに中断状態にされるので、実機でこの状態にするのは難しい)。カメラを使う場合には、このイベントでリソースの解放/取得を行う(そのコードは別途公開のサンプルコードを参照してほしい)。なお、ここでカメラの表示に使っているMediaCaptureクラスはアプリが非表示になったときに内部的にカメラを解放してしまうので、そのようにしないとアプリの再表示時にプレビューが表示されなくなってしまう。
中断/再開したとき(Windows Phone 8.1)
別途公開のサンプルコードはユニバーサルWindowsアプリである。Windowsでも動作するが、ここではPhoneの画像を示す。
アプリを中断した後に再開すると、このようなイベントが発生する。アプリが非表示になってしばらくすると、システムがアプリを中断させるので、非表示のイベントの後で中断のイベントが発生している。そして、エンドユーザーがアプリを再びフォアグラウンドに持ってきたときに、再開と表示のイベントが発生する。
非表示にされるときにリソースを解放していれば、中断時のイベントで解放する必要はない(すでに解放済み)。同様に、表示されるときにリソースを取得しているならば、中断からの再開時には取得しなくてよい。カメラを使う場合には、中断/再開のイベントではリソースの解放/取得処理を行わず、非表示/表示のイベントだけで解放/取得すればよいのである。
まとめ
中断からの再開時にコンテンツを再取得するような処理は、ApplicationクラスのResumingイベントで行う。リソースを解放/再取得する処理は、そのタイプによって、ApplicationクラスのSuspendingイベント/Resumingイベント(中断/再開時)か、WindowクラスのVisibilityChangedイベント(非表示/表示時)を利用するとよい。
Copyright© Digital Advantage Corp. All Rights Reserved.