本サイトからスピンオフした姉妹サイトです。iOS2015年5月11日Androidアプリでは別アプリ移動時に画面が破棄され、アプリ再表示時に画面が復元される場合がある。この画面の再生成を、MvxViewModelのライフサイクルメソッドにより行う方法を説明する。→Androidでは、実行しているアプリを切り替えた場合、それまで実行中であったアプリの画面やアプリ自体のプロセスを破棄してメモリを確保することがある。破棄されたアプリ画面は、別のアプリからタスクスイッチやバックキーなどで戻ってきたときに再生成される仕組みなので、画面を離れる際に元の状態を保存しておき、画面を復元できるようにしておくことが必要となる。MvvmCrossを使ったAndroidアプリ開発では、プラットフォームごとに発生する画面の再生成に対して*1、MvxViewModelクラスに用意された機能で対応できるようになっている。今回は、そのMvxViewModelクラスに用意されたライフサイクルメソッドを使用して、画面の再生成に備える方法について解説する*2。Phoneでも同様の実装が必要になる。iOSでは別のアプリに移動してメモリが不足した場合、バックグラウンドのアプリは終了し、再度、アプリが通常起動する。Studioでも同様の手順で、本稿の内容が実現できることは確認している)。MvxViewModelのライフサイクルMvxViewModelは通常、MvxViewController(iOS)やMvxActivity(Android)といったViewの生成時に、自動的にインスタンス化されるが、その際、図1の流れで処理が呼ばれていく。MvxViewModelのライフサイクルまずは、通常の起動処理を見ていこう。画面が生成されてViewModelが生成される時には、通常のクラスと同じようにコンストラクターが呼ばれる(1)。ここでは主にIoCコンテナー(DIコンテナー)に入っているプラグインやServiceのオブジェクトを取得する操作を記述する。次に、Initメソッド(もしくはInitFromBundleメソッド)が呼ばれる(2)。これは「Tips:MvvmCrossで画面遷移するには?」でも紹介した通り、画面遷移時に渡されたパラメーターを受け取ることができるメソッドだ。全般的な初期化もこのタイミングで行える。その後、Startメソッドが呼び出され(3)、表示に必要なデータがそろった状態で画面表示の準備処理を行うことができる。Startメソッドを抜けた後は、実際の画面表示に向けて、各プラットフォームのライフサイクルイベントが発生していき、実際に画面が表示される。各プラットフォームのライフサイクルイベントに対応する処理はMvxViewModelクラスには実装がないが、コマンドバインディングでイベントをキャッチすることが可能だ。この点については「Tips:MvvmCrossでコマンドバインディングするには?」の「【コラム】UIViewController(iOS)やActivity(Android)のライフサイクルを監視する」を参照してほしい。その後、別の画面に遷移する場合など、状態の保存が必要になったタイミングでSaveStateメソッド(もしくはSaveStateToBundleメソッド)が呼び出される(4)。ここでは画面に持っている状態をオブジェクトに保存して返す。すると、MvvmCrossは各プラットフォーム固有の情報保存処理に対してオブジェクトのデータを保存する。別のアプリに移ったりメモリが不足したりするなどして画面が破棄された後に画面に戻ってきた場合は、再度、画面生成が行われ、ViewModelも「コンストラクター」「Initメソッド」と順番に呼び出されていくが、InitメソッドとStartメソッドの間でReloadStateメソッド(もしくはReloadStateFromBundleメソッド)が呼び出される(5)。このメソッドにSaveStateメソッドにより保存された値(=オブジェクトのデータ)が入っているので、この値を使ってViewModelの状態を復元する。サンプルで動きを確認するでは、実際にサンプルを使って動きを確認してみよう。「Tips:MvvmCrossのプロジェクトをセットアップするには?」の手順に従い、MvvmCrossプロジェクトを作成する。ソリューション名は「CrossStateSample」と設定する。今回はAndroidでの動きを見るのが主目的のため、iOS用のTouchプロジェクトは作成しなかった。その後、「Tips:MvvmCrossで画面遷移するには?」の「画面追加と画面遷移の実装」を参考にSecondViewModelとSecondViewを作成し、FirstViewModeとFirstViewを変更していく。その手順は以下の通りだ。CrossStateSample.CoreプロジェクトのViewModelフォルダーにSecondViewModel.csを追加し、次のように編集する。_backCommand;}}BackCommandを実装する(SecondViewModel.cs)FirstViewModelはデフォルトのHelloプロパティを残した状態でGoToSecondCommandプロパティを追加する。具体的には次のように編集する。--}}GoToSecondCommandを実装する(FirstViewModel.cs)次に、CrossStateSample.DroidプロジェクトのResources-layoutフォルダーにあるFirstView.axmlファイルを開き、次のように編集する。--></LinearLayout>レイアウトの編集(SecondView.axml)デフォルトのものからButtonを追加している。SecondView.axmlファイルを追加し、次のように編集する。/></LinearLayout>レイアウトの編集(SecondView.axml)FirstViewに戻るボタンを配置する。最後にSecondView.axmlファイルに対応するViewクラスを作成する。ViewsフォルダーにSecondView.csファイルを追加し、次のように編集する。bundle){base.OnCreate(bundle);SetContentView(Resource.Layout.SecondView);}}}Viewクラスの実装(SecondView.cs)この状態で実行すると、テンプレートのHello変数に対してバインディングされたテキストと、SecondViewへの遷移ボタンがある状態で起動する。SecondView]ボタンをタップしてSecondViewに移動した後、FirstViewに戻った際も表示されているテキストは保持されていることが分かる。ホーム画面に戻ったりしても保持されている(図2)。具体的な対応はなされていないものの、まだアプリはメモリ内にあり、Viewやプロセスが破棄されていないため、状態が保持されていることとなる。サンプルの画面遷移メモリが十分にあれば未対策でもViewModelの値は保持される。状態保存のテストのためにAndroidの設定を変更するアクティビティ(View)の破棄やプロセスの停止は、アプリをバックグラウンドにした状態で他のアプリで大量のメモリを使用したり、アクティビティの遷移階層を深くしたりすることで発生する。現在流通している端末はスペックが上がっており、アクティビティ破棄に至るまでには動画再生やゲームなど重いタスクのアプリを複数本起動することを繰り返す必要がある。デバッグのためにこれらの操作をするのはかなりの手間となってしまう。デバッグの効率化を目的として、Androidの開発者オプションにはアクティビティ破棄やプロセス停止を任意で発生させることができる設定が用意されている。processes)などに切り替えたりする(図3)。SO-02G」のもの)。[アクティビティを保持しない]のオプションを使用すると、ホーム画面に戻るなどユーザーがアプリから離れた際、表示されていなかったアクティビティが破棄される。[バックグラウンドプロセスの上限]を[バックグラウンドプロセスを使用しない]などにすると、ユーザーがアプリから離れた瞬間にプロセスが停止する。両者の差は、アクティビティ以外のオブジェクトに影響があるかどうかである。アクティビティの破棄はViewとViewModelにのみ影響し、それ以外の状態はメモリに保持される。プロセスの停止はMvvmCrossの内部クラスやプラグイン、ServiceクラスなどのIoCコンテナーに入っているオブジェクトなども含めて初期化される。テストの際はどちらのパターンでも正常に復帰できることを確認することが重要となる。実際に[アクティビティを保持しない]の設定を行った端末で先ほどのサンプルを確認する(図4)。SecondView]をタップしてSecondViewを開いた状態で端末のホーム画面に戻る。MvvmCross」に戻っていることが分かる。先ほどのサンプルを、[アクティビティを保持しない]設定で動かすと、入力されたテキストが初期化されてしまっていることが分かるなお、[アクティビティを保持しない]や[バックグラウンドプロセスの上限]の設定が有効な場合、アプリによっては復帰に時間がかかる場合や一部情報が失われるなど、実際の利用には不向きなため、確認が終わったら元の設定に戻すことを強く推奨する。状態を保存し、保存した状態から画面を復元する状態を保存するためには、保存する値を保持するためのクラスを用意し、状態を保存したそのクラスのインスタンスを返すSaveStateメソッドを実装し、状態を復元するためのReloadStateメソッドを実装する。FirstViewModel.csファイルを開き、次のように編集する。--}}画面状態の保存・復元処理を追加する(FirstViewModel.cs)まず、保存する値を保持するSavedStateクラスを用意する(1)。状態保存が必要になったタイミングでSaveStateメソッドが呼ばれるので、SavedStateクラスのインスタンスを作成して保存する値を格納する(2)。Viewが再生成されると、ReloadStateメソッドが呼ばれるので(3)、ここで引数に渡されたSavedStateオブジェクトから必要なデータを復元する。この状態でアプリを実行すると、先ほど行ったView破棄の操作でも入力された内容が保持されていることが分かる。SaveState/ReloadStateメソッドを実装することで、[アクティビティを保持しない]設定でも値が保持されていることが分かるなお、状態保存に使用するクラスに使用できる型だが、「Tips:MvvmCrossで画面遷移するには?」の「パラメーターの型について」で記載したものと同じ制限を受ける。メソッドを使用する「Tips:MvvmCrossで画面遷移するには?」で紹介したInitメソッドと共に、SaveStateメソッド、ReloadStateメソッドは継承関係ではなく独自の定義を行っている。これはMvvmCrossがリフレクションでこれらのメソッドを検索しているためだ。どのメソッドも実際には値をMvxBundleクラスのオブジェクトから値を読み込んで引数として与えたり、戻り値をMvxBundleオブジェクトに書き込んだりしている(利用できる型の制限が共通なのは、共通のMvxBundleクラスを使用しているからである)。独自にメソッドを定義する以外に、MvxViewModelクラスには、InitFromBundle/SaveStateToBundle/ReloadStateFromBundleメソッドなどがすでに用意されていて、これらをオーバーライドして用いることができる。string>型のプロパティを持っており、これを直接取り扱うことも可能だ。今回のサンプルで、SavedStateクラスやSaveStateメソッド、ReloadStateメソッドを用いずに、次のように書くこともできる。state.Data["Hello"];}状態を保持するクラス(今回の例ではSavedStateクラス)を使う代わりに、MvxBundleクラスのDataプロパティを直接使用する場合のコード例MvxBundleを直接操作するので処理の記述は手間となるが、複雑なデータ型を保存する場合や独自にシリアライズ機能を持っている型を扱う場合などは選択するとよいだろう。メソッドはIDEの入力補完で入力できるので、その点を魅力に感じる人もいるだろう。※以下では、本稿の前後を合わせて5回分(第47回~第51回)のみ表示しています。INDEX]を参照してください。MvvmCrossで画面遷移するには?MvvmCrossでiOS/Androidアプリの画面遷移をするための基本的な実装方法を説明する。Xamarin.Formsでプラットフォームごとの微調整を行うには?カスタムレンダラーやDependencyServiceの仕組みを使わず、Deviceクラスを利用してプラットフォーム間で異なる部分を微調整する方法を説明する。MvvmCrossでAndroidの画面の再生成に対応するには?Androidアプリでは別アプリ移動時に画面が破棄され、アプリ再表示時に画面が復元される場合がある。この画面の再生成を、MvxViewModelのライフサイクルメソッドにより行う方法を説明する。Xamarin.Formsでローカルデータベースを使用するには?アプリを終了して再起動したときに、ユーザーデータを復活させたい場合、ローカルやクラウドにデータを保存することになる。その一つの方法として、SQLite.Netを使ってローカルDBに保存する方法を説明する。MvvmCrossでカスタムコンバーターを作成するには?MvvmCrossでのiOS/Androidアプリ開発において、バインディングする値を変換できるカスタムコンバーターの使い方を説明する。機械学習エンジニアを目指す人~初中級者のための技術情報サイトです。「機械学習&AI」「クラウド&ビッグデータ」「IoTデバイス」のネット記事情報を共有中。開発者のためのSlackチーム、作りました:「機械学習&AI」「クラウド&ビッグデータ」「IoTデバイス」のネット記事情報を共有中。2020最適なビューへの切り替えを推奨します:スマートフォン用表示に。