Android Kotlin基礎講座 05.1: ViewModelとViewModelProvider
目次
タスク:GameViewModelを作成する
ViewModelクラスはUIに関連するデータを保存・管理するためにデザインされています。このアプリにおいて、それぞれのViewModelは一つのフラグメントに関連しています。
このタスクでは、最初のViewModelをアプリに追加します。GameFragment用のGameViewModelです。ViewModelがライフサイクル対応であるということが何を意味するかについても学習していきます。
ステップ1:GameViewModelクラスを追加する
- build.gradle(module:app)ファイルを開いてください。dependenciesブロックの中に、ViewModel用のGradle依存関係を追加してください。
最新バージョンのライブラリを使っていればアプリは問題なくコンパイルされます。もし使っていない場合は、問題の解決を試みるか、以下に示されているバージョンに戻してください。
//ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
- パッケージに含まれるscreens/game/フォルダーの中に、GameViewModelというKotlinクラスを新規作成してください。
- GameViewModelクラスをViewModelという抽象クラスを継承させてください。
- ViewModelがどのようにライフサイクル対応なのかを理解する手助けとするために、log文を含むinitブロックを追加してください。
class GameViewModel : ViewModel() {
init {
Log.i("GameViewModel", "GameViewModel created!")
}
}
ステップ2:onCleared()をオーバーライドし、ログを追加する
ViewModelはそれに関連するフラグメントが取り外されたとき、またはアクティビティが終了したときに破棄されます。ViewModelが破棄される直前にonCleared()コールバックがリソースをクリーンアップするために呼び出されます。
- GameViewModelクラスの中で、onCleared()メソッドをオーバーライドしてください。
- onCleared()の中にGameViewModelのライフサイクルをトラッキングするためのログ文を追加してください。
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed!")
}
ステップ3:GameViewModelをGameFragmentに紐づける
ViewModelはUI controllerに紐づけられる必要があります。それら二つを紐づけるためには、UI controllerの中でViewModelの参照を作成します。
このステップでは、GameViewModelの参照をそれに対応するUI controllerであるGameFragmentの中に作っていきます。
- GameFragmentクラスの中に、GameViewModel型のクラス変数をフィールドとして一番上の部分に追加してください。
private lateinit var viewModel: GameViewModel
ステップ4:ViewModelを初期化する
画面回転のようなコンフィグレーション変化の間、フラグメントのようなUI controllerは再生成されます。しかしながら、ViewModelインスタンスは引き継がれます。もしViewModelインスタンスをViewModelクラスを使って作っていると、フラグメントが再生成される度に、新しいオブジェクトが作られることになります。ですのでViewModelインスタンスはViewModelProviderを使って作成しましょう。
重要:ViewModelオブジェクトを作る際は、ViewModelのインスタンスを直接初期化するのではなく、必ずViewModelProviderを使うようにしてください。
ViewModelProviderの機能:
- ViewModelProviderはViewModelが既に存在している場合、そのViewModelを返します。存在しない場合は新しく作成します。
- ViewModelProviderは与えられたスコープ(アクティビティやフラグメント)に紐づいたViewModelインスタンスを作成します。
- 生成されたViewModelはスコープが有効である限り、保持されます。例として、スコープがフラグメントである場合、ViewModelはフラグメントが取り外されるまで保持されます。
ViewModelProviderを作成するためのViewModelProvider.get()メソッドを使い、ViewModelを初期化します。
- GameFragmentクラスの中で、viewModel変数を初期化してください。以下のコードをonCreateView()中のbinding変数の定義のあとに追加してください。ViewModelProvider.get()メソッドを使い、関連するGameFragmentコンテクストとGameViewModelクラスを引数に渡します。
- ViewModelオブジェクトの初期化の前に、ViewModelPrivider.get()メソッドが呼び出されていることを確認するためのログ文を追加してください。
Log.i("GameFragment", "Called ViewModelProvider.get")
viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
- アプリを起動してください。Android StudioでLogcatを開き、”Game”でフィルタリングしてください。実機、またはエミュレーターでPlayボタンをタップしてください。ゲーム画面が開きます。
Logcatに示されているように、GameFragmentのonCreateView()メソッドがViewModelPrivider.get()メソッドを呼び出し、GameViewModelを作成します。GameFragmentとGameViewModelに追加したログ文がLogcatに表示されます。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created!
- 実機またはエミュレーターの画面の自動回転設定を有効にし、画面の向きを数回変えてください。毎回GameFragmentが破棄され再生成されます。そのため、ViewModelPrivider.get()も毎回呼び出されます。しかしGameViewModelは一度しか生成されておらず、毎回の呼び出しで再生成や破棄されたりはしていません。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get
- ゲームを終了するかゲーム画面の外に遷移してください。GameFragmentは破棄されます。関連するGameViewModelも破棄され、onCleared()コールバックが呼び出されます。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel destroyed!