概要
今まで onSaveInstanceState をシコシコ書いて頑張っていた人向けの Android Architecture Components の ViewModel 解説記事です。
ちなみに ViewModel の公式の記事は以下になります。
より詳しく知っている人からの突っ込み歓迎です。
背景
なんでこの記事を書こうと思ったかというと、自分が理解に超絶苦しんだからです;;
Android Architecture Components の ViewModel は、画面回転時などの Activity 再構築に対処するための技術です。
同様の対応方法の一つに Activity#onSaveInstanceState があります(もうひとつ方法があるじゃん!と突っ込みを入れたくてうずうずしている方はこの記事の想定読者ではありませんゾ!)。
Activity#onSaveInstanceState による対処法は制約が多く実装もちょっと複雑です。
例えば扱える情報には Percerable や Serializable といった制約があります。これは一時的なディスクへの保存に由来する制約です。また、復元されたオブジェクトは再構築前のオブジェクトとは別インスタンスになるという点も注意が必要です。
一方で ViewModel クラスによる方法は制約も少なく実装も簡単です。
私は Activity#onSaveInstanceState には色々と制約があったためかなり身構えました。また、いったいどうやって実現されているのかまったく想像ができず、やっていいこと・わるいことの判断がつきませんでした。
そのあたりを踏まえた解説をしたいと思います。
ViewModel はディスク保存ではない!
まず最初に ViewModel はディスク保存ではありません。単純に ViewModel は Activity の再構築時にも壊されることなくメモリ上に残り続けることができるオブジェクトだと思っていいと思います。そのため Percerable や Serializable 等の制約は一切ありません。Activity 再構築前と再構築後で変わることのない全く同一のインスタンスです。当然ながらインスタンスが持つ各種参照は維持されます。
このことは公式ページの「The lifecycle of a ViewModel」の説明のところに記載がありますが、Activity#onSaveInstanceState の先入観に捕らわれた状態では理解できませんでした。
ViewModel は Activity の再構築を越えることのできる長寿オブジェクトなのです!
どうやって Activity の再構築を越えることができるのか?
そんなのどうやってるの?という疑問が湧きますよね。湧きませんか?実はその方法はかなり昔からあるようなのです。Android 3.0 の時代(API level 11)からです。
その方法とは Fragment#setRetainInstance メソッドを使う方法です。公式のドキュメントでも onSaveInstanceState と並んで紹介されています。
私はこの方法を知りませんでした。
ViewModel の実装を読み進めていくと、この Fragment#setRetainInstance メソッドを使った Fragment が出てきます。Fragment#setRetainInstance メソッドが何か怪しいと思って調べたところ、これが onSaveInstanceState に並ぶ別の方法であることが分かりました。
ちなみに Fragment#setRetainInstance は、Fragment を Activity の再構築時に破壊されないようにするための技術なので、汎用的にオブジェクトを格納するためには周辺の実装を色々と自分で作らないといけません。面倒ですね。
そこで ViewModel です。要は ViewModel は Fragment#setRetainInstance を使った方法を、簡単にそして便利に使えるようにしたラッパーなんですね!
メモリ上に存続し続けるオブジェクトなので!
これまでの説明で ViewModel の正体がわかったでしょうか?
onSaveInstanceState の方法とは違い Fragment#setRetainInstance を使ったメモリにオブジェクトを存続し続けるための技術です。
ということは…。
- onSaveInstanceState とは違って別に ViewModel は Percerable や Serializable である必要はありません。
自由に作れます。参照も維持されます。
ですが、何でも自由なわけではないです。作る際に以下の制約があります。当然、公式のドキュメントにも書いてあります。
- ViewModel よりも寿命が短いオブジェクトの参照を持ち続けてはなりません。
ViewModel は再構築される Activitiy よりも寿命が長いです。もしも Activity の参照を ViewModel が持ってしまったら、寿命が切れた Activity の参照を持ち続けてしまいます。それは(実際には確かめてはいないが)Activity がガーベジコレクションの対象から外れることを意味します。メモリ上に残ったままになってしまうということです。
なので Activitiy や Fragment や View 等を参照してはならないわけです。
また、ディスクに退避されることがなく、別のアプリにフォーカスが移動した際もメモリ上に展開されたままになると想像できるので、大量のメモリを消費するのも問題かもしれませんね。
以上です!