Xamarin.Forms の App Lifecycle に Windows RT 対応させたい
前回のセッションデータ復元で見事に失敗してしまったので、リベンジすべく Windows ランタイムアプリのライフサイクルに合わせて Xamarin.Forms App Lifecyle の各イベントハンドラが呼ばれるようにしたいと思います
まぁ、そのうち正式リリースなどで対応されると思いますので臨時対応的な手法ですが
スポンサードリンク
まずは Windows ランタイムの状態遷移のおさらい!
前回は中断とシャットダウンで OnSleep() が呼ばれていたので、Application.Suspending の対応はできていると思います
通常の起動時は OnStart() を呼び、Terminated からの起動時や Application.Resuming イベントで再開(Activated)された場合のみ OnResume() を呼べばよさそうですね
というわけで登場するのは
ご存じ Prism.PubSubEvents こと EventAggregator さんです! NuGet ソリューションパッケージの管理のメニューから一発インストール
インストールが終わったら Windows RT と Xamarin.Forms の間でやりとりするイベントを PCL の共通コード側に追加
中身はこんな感じ・・・スッカスカで大丈夫です
amespace XamarinSessionRestore.Events { /// <summary> /// アプリケーション状態遷移の列挙値 /// </summary> public enum ApplicationState { /// <summary> /// 起動中 /// </summary> Starting = 0, /// <summary> /// 中断中 /// </summary> Sleeping = 1, /// <summary> /// 再開中 /// </summary> Resuming = 2, } /// <summary> /// アプリケーション状態遷移イベント /// </summary> public class ApplicationStateChangedEvent : PubSubEvent<ApplicationState> { } }
さらに Xamarin.Forms 側の App.cs を修正します
/// <summary> /// アプリケーション基盤クラス /// </summary> public class App : Application { /// <summary> /// セッションデータリポジトリ /// </summary> private ISessionRepository sessionRepository = null; /// <summary> /// DI コンテナ /// </summary> public static UnityContainer Container = new UnityContainer(); /// <summary> /// コンストラクタ /// </summary> static App() { // EventAggregatorを登録 Container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager()); // アプリケーション状態遷移イベントを購読 Container.Resolve<IEventAggregator>().GetEvent<ApplicationStateChangedEvent>().Subscribe( (e) => { switch (e) { // 起動時 case ApplicationState.Starting: ((App)App.Current).OnStart(); break; // 中断時 case ApplicationState.Sleeping: // とりあえず何もしない break; // 再開時 case ApplicationState.Resuming: ((App)App.Current).OnResume(); break; } }); } ~ 中略 ~ }
ライフサイクル管理というメタアプリ的な処理が必要なので、static コンストラクタ内で EventAggregator の登録とイベントの購読設定をしています
イベントがどこかで発行されたらそれを受信して対応するアプリケーション状態遷移のイベントハンドラを強制的に呼んでるだけ
あとは Windows RT 側の App.xaml.cs を修正します(これは Windows ストアアプリと Windows Phone 8.1 でほぼ共通コード)
/// <summary> /// 既定の Application クラスに対してアプリケーション独自の動作を実装します。 /// </summary> public sealed partial class App : Application { private TransitionCollection transitions; /// <summary> /// イベントアグリゲーター /// </summary> private IEventAggregator eventAggregator = null; /// <summary> /// 単一アプリケーション オブジェクトを初期化します。これは、実行される作成したコードの /// 最初の行であり、main() または WinMain() と論理的に等価です。 /// </summary> public App() { this.InitializeComponent(); this.eventAggregator = XamarinSessionRestore.App.Container.Resolve<IEventAggregator>(); this.Suspending += OnSuspending; this.Resuming += (s, e) => { // 再開した場合は Xamarin.Forms App Lifecycle に伝える this.eventAggregator.GetEvent<ApplicationStateChangedEvent>().Publish(ApplicationState.Resuming); }; XamarinSessionRestore.App.Container.RegisterType<ISessionRepository, XmlRepositories>(new ContainerControlledLifetimeManager()); } /// <summary> /// アプリケーションがエンド ユーザーによって正常に起動されたときに呼び出されます。他のエントリ ポイントは、 /// アプリケーションが特定のファイルを開くために呼び出されたときに /// 検索結果やその他の情報を表示するために使用されます。 /// </summary> /// <param name="e">起動要求とプロセスの詳細を表示します。</param> protected override void OnLaunched(LaunchActivatedEventArgs e) { ~ 中略 ~ // 現在のウィンドウがアクティブであることを確認します Window.Current.Activate(); // 起動したことを Xamarin.Forms App Lifecycle に伝える if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { // Terminated からの起動は再開扱い this.eventAggregator.GetEvent<ApplicationStateChangedEvent>().Publish(ApplicationState.Resuming); } else { this.eventAggregator.GetEvent<ApplicationStateChangedEvent>().Publish(ApplicationState.Starting); } } ~ 中略 ~ }
変えているのはコンストラクタで eventAggregator を解決して Application.Resuming イベント発生時に Resuming を通知していることと、Window.Current.Activate の後にアプリ起動時の状態遷移通知を分岐させているところです
LaunchActivatedEventArgs.PreviousExecutionState が Terminated だった場合は Terminated からの復帰なので、起動時でも Resuming を通知しています
さてこれでリベンジ実行
復元できてますね
App.OnStart: activated. TopPage.xaml.OnAppearing: TopPage Appearing. App.OnStart: Restore session data. App.OnSleep: sleeped. TopPage.xaml.OnAppearing: TopPage Appearing. App.OnResume: Restore session data. App.OnResume: resumed.
ちゃんと OnSleep 以外も呼ばれるようになりました