【Android Architecture Components】ViewModel vs SavedInstanceState

[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)のドキュメントに[ViewModel vs SavedInstanceState](https://developer.android.com/topic/libraries/architecture/viewmodel.html#viewmodel_vs_savedinstancestate)と言う項目が増えてました。

結論から言うと、

**[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)は```onSaveInstanceState```の代替えではない**

と言う事です。

[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)を使ったとしても、引き続き```onSaveInstanceState() ```でデータを保存する必要があります。


# [ViewModel vs SavedInstanceState](https://developer.android.com/topic/libraries/architecture/viewmodel.html#viewmodel_vs_savedinstancestate)


要約するとこんな感じです。

[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)は設定の変更によって画面が再生成される場合はデータを保持しますが、メモリ不足などでAndroid OSからKillされた場合はViewModelも一緒に破棄されます。
つまり、ViewModelに保存していたデータも消えてしまいます。

一方で、```onSaveInstanceState()```でBundleに保存したデータは、システムのプロセスメモリに保存されるので、Android OSによってアプリがKillされた場合でもデータは保持され続けます。
ただし、Bundleに保存できるデータには上限があるので、最小限のデータのみ保存してください。
例えば、国情報を表示する画面がある場合は、[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)に画面に表示する国情報を保存し、```onSaveInstanceState```のBundleには国のIDのみを保存するようにしましょう。


# 画面が生成されるパターンとフロー

画面が生成されるパターンは３つあり、以下のようなフローで実装します。

### 最初にActivityが生成された時

この場合、```onSaveInstanceState```のBundleデータなし、ViewModelも空の状態となります。。
ViewModelを生成してからの空のクエリをViewModelに渡して、ViewModelはクエリが空なのでデータのロードはしません。
Activityは初期化状態で始まります。

### Android OSによってKillされた後に、再度アプリを表示した時

Activityは```onSaveInstanceState```でBundleに保存したクエリをもっていますが、ViewModelに保存したデータは消えています。
ActivityはそのBundleからクエリを取り出し、ViewModelに渡します。
ViewModelはデータが空なので、クエリを元にデータをロードするようにします。

### 設定の変更によってActivityが再生成された時

Activityは```onSaveInstanceState```でBundleに保存したクエリとViewModelの保存した検索結果をもっています。
```onSaveInstanceState```でBundleに保存したクエリをViewModelに渡して、ViewModelはデータを取得し直す必要かどうかを検討します。


# どうやって解決するか？

```Bundle``` に対応した```ViewModelProvider.Factory```を作成します。

```java
class MyModelFactory implements ViewModelProvider.Factory {
   private final Bundle bundle;
   public MyModelFactory(Bundle bundle) {
      this.bundle = bundle;
   }
   @Override
   public MyViewModel create(Class modelClass) {
      MyViewModel viewModel = new MyViewModel();
      viewModel.readFrom(bundle);
      return viewModel;
   }
...
```

この```ViewModelProvider.Factory```はViewModelの生成と最後に渡した```Bundle```を保持する機能があります。

```Bundle```にデータを書き込むには従来通り、Activity/Fragmentで```onSaveInstance ```をオーバーライドします。

```
@Override
public void onSaveInstanceState(Bundle bundle) {
    super.onSaveInstanceState(bundle);
    viewModel.writeTo(bundle);
}
```

汎用的に使えるように、```MyModelFactory```のViewModelをジェネリクスで定義します。

```
class BundleAwareViewModelFactory<T extends ParcelableViewModel> implements ViewModelProvider.Factory {
    private final Bundle bundle;
    private final ViewModelProvider.Factory provider;
    public BundleAwareViewModelFactory(@Nullable Bundle bundle,
                                       ViewModelProvider.Factory provider) {
        this.bundle = bundle;
        this.provider = provider;
    }
    @SuppressWarnings("unchecked")
    @Override
    public T create(final Class modelClass) {
        T viewModel = (T) provider.create(modelClass);
        if (bundle != null) {
            viewModel.readFrom(bundle);
        }
        return viewModel;
    }
}
```

すべてのViewModelは下記のViewModelを継承するようにします。

```
public abstract class ParcelableViewModel extends ViewModel {

    public abstract void writeTo(@NonNull Bundle bundle);
    public abstract void readFrom(@NonNull Bundle bundle);
}
```

## 参考

* [Of Bundles and ViewModels](https://proandroiddev.com/customizing-the-new-viewmodel-cf28b8a7c5fc)
* [ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders](https://medium.com/google-developers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090)