ViewModelのデータを保存/復元するにはSavedStateHandleを使う

# こうだ

![saved_state_handle.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82681/2c5f33fb-2395-a7e4-8c46-c69dda5b15e6.gif)

※`Developer options` の `Don't keep activities` を有効にしています

# Androidとメモリー上のデータ

Androidはすぐメモリー上のデータが飛びます。
画面を回転したら飛ぶ、システムがアクティビティを破棄したら飛ぶ、システムがプロセスをキルったら飛ぶ。

これらはAndroidのライフサイクルのせいで、上記のタイミングでActivityやFragmentのインスタンスが都度作り直しになるためです。
そのために開発者はActivityやFragmentの `onSaveInstanceState`関数で飛ばしたくないデータを保存し、 `onCreate` 関数などで `savedInstanceState` からデータを復元する処理を書かなくてはいけませんでした。

# ViewModelの登場

AndroidでViewModelクラスが登場して以降、開発者は様々な恩恵を受けられるようになりました。

その一つにメモリー上のデータが飛びにくくなったことがあります。
ViewModelは画面を回転させてもデータは飛びません！　自動で復元されます！
やったね！……？

# ViewModelの問題

しかしViewModelといえど、システムがアクティビティを破棄した時、システムがプロセスをキルした時はやはり飛びます。No, やったね！。やってない。

結局、ViewModle内の状態も `savedInstanceState` に都度保存・から都度復元しなければならないのでしょうか？

# SavedStateHandle

いいえ、そんなことはありません。ViewModelには `SavedStateHandle` という便利なものが用意されています。

`SavedStateHandle` はViewModel内のプロパティの保存/復元を行うためのヘルパークラスです。
`SavedStateHandle` の内部でプロパティの実体を保持しており、ライフサイクルを勝手に観測していい感じに保存・復元をしてくれます。

ここで例を見てみましょう。
保存/復元を行いたいのは `EditText` とデータバインディングされた `val userTextInput: MutableLiveData<String>` プロパティであるとします。 
保存/復元を考えない場合、 `userTextInput` は `val userTextInput = MutableLiveData<String>("")` のように宣言されていたことでしょう。
これをSavedStateHandleを使って保存/復元するには以下のように書きます。

```MainFragment.kt
class MainFragment : Fragment() {
    ...
    
    // SavedStateHandleを使っても使わなくてもViewModelの初期化方法は同じ。
    private val viewModel: MainViewModel by viewModels()

    ...
}
```

```MainViewModel.kt
class MainViewModel(
    // ViewModelの第1引数にSavedStateHandleを指定します
    handle: SavedStateHandle
) : ViewModel() {
    // SavedStateHandleからキー"USER_TEXT_INPUT"にひも付くMutableLiveDataを探して引っ張ってくる。
    // 存在しない場合は新しく作る（そしてSavedStateHandle内でキー"USER_TEXT_INPUT"に対してひも付け、抱えておく）。
    // 初期値は空文字。
    val userTextInput: MutableLiveData<String> = handle.getLiveData<String>("USER_TEXT_INPUT", "")
```

普通にMutableLiveDataのコンストラクタから宣言していたのを `handle` の関数から取得するようにしただけです。
これだけでシステムがアクティビティを破棄する時、システムがプロセスをキルする時はその時のuserTextInputの値を勝手に保存してくれますし、復帰する時は勝手に復元してくれます。

`val userTextInput = MutableLiveData<String>("")` （Simple LiveData）方式と `val userTextInput: MutableLiveData<String> = handle.getLiveData<String>("USER_TEXT_INPUT", "")
` （SavedStateHandle#getLiveData）方式の振る舞いを比較したのが冒頭の動画になります。
`EditText` に文字を入力したあとホームボタンを押してランチャー画面を表示させ、再度アプリアイコンをタップしてアプリを起動させたところ、Simple LiveDataの方は入力したデータが飛んでしまっていますが、SavedStateHandle#getLiveDataの方はきちんとデータが残っています。これは保存/復元が行われたということです。今度こそやったね！

なお、`handle.getLiveData<T>(...)` の `T` になれるのは、

* 基本型
    * Boolean
    * Short
    * Int
    * Long
    * Float
    * Double
    * String
    * Byte
    * Char
    * CharSequence
* Parcelable
* Array<基本型 または Parcelable>
* Serializable
* Binder
* Bundle
* Size
* SizeF
* ArrayList<`T`になれるどれか>
* SparseArray<`T`になれるどれか>

のみです。
`T` がエンティティクラスの場合はParcelableを継承し実装するのがよいでしょう。

# ちなみに

`SavedStateHandle` は `get` という関数も持っています。
これは `MutableLiveData<T>` ではなく生の値（`T`）を返す関数です。
使う機会は多くないかもしれませんが覚えておくといいでしょう。

# おわりに

Androidのライフサイクルに泣きたくなるのをこらえて頑張りましょう！

# 追記

続きのような立ち位置の記事を書きました。合わせてどうぞ。

[Repositoryから取得したLiveDataをViewModelでキャッシュしておくには？
](https://qiita.com/que9/items/8db44885d832c6a3c6b8)