ViewModel考察

# 本記事を書く経緯

ViewModelをよくこんな感じに使っていましたが

```kotlin
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }
}
```

これはある程度の「魔術」だと思って、`viewModels`の中で何をやっているのか、なんでActivityのライフサイクルを超えて生存できるのかを気にしていませんでしたが、Activity間で同じViewModelを共有したい要件に出会ったので、本格的にViewModel周りを調査してみました。

# ViewModelの生成

まずAndroid KTXを使わないでViewModelを利用する原始的な書き方を見てみます。

```kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // (Deprecated) viewModel = ViewModelProviders.of(this).get(MainViewModel::class)
        viewModel = ViewModelProvider(this).get(MainViewModel::class)
    }
}
```

Android KTXが出る前まではたぶん一番こういう書き方がよく見られます。
ただ、この書き方はデフォルトで用意されている`ViewModelProvider.Factory`を使用する前提になっているし、登場人物はまだ出揃っていないので、解説には不向きです。もうちょっとカスタマイズした書き方を見てみます。

```kotlin
// 2. Activityは元からViewModelStoreOwnerを実装しています
class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // 1. ViewModelProvider(owner: ViewModelStoreOwner, factory: ViewModelProvider.Factory)
        val provider = ViewModelProvider(this, MainViewModelFactory())
        viewModel = provider.get(MainViewModel::class)
    }
}

// 3. 実際にViewModelを生成するFactoryクラス
class MainViewModelFactory : ViewModelProvider.Factory() {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        // ViewModelを手動生成する
        return MainViewModel() as T
    }
}

class MainViewModel : ViewModel() {
    ...
}
```

上記の**ViewModelProvider**、**ViewModelStoreOwner**、**ViewModelProvider.Factory**は`ViewModel`の取得に関わる主要人物です。一個ずつ見ていきます。


# ViewModelStoreOwner

```java
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}
```

`ViewModelStoreOwner`は`ViewModelStore`を返す役割を持っています。`ViewModel`の**保存**に関わっています。
`ViewModelStore`は自体一種のHashMapで、`ViewModel`を保存/返す役割を持っています。

`ViewModelStoreOwner`が返す`ViewModelStore`は毎回違うのか、それとも同じ`ViewModelStore`を返すかは`ViewModelStoreOwner`の実装次第です。
Activityはこの`ViewModelStoreOwner`を実装しています。回転などで再生成されたActivityでも一律前と同じ`ViewModelStore`のインスタンスを返すように実装されているので、Activityのライフサイクルを超えて`ViewModel`は生存できました。

# ViewModelProvider.Factory

```java
public interface Factory {
    /**
     * Creates a new instance of the given {@code Class}.
     * <p>
     *
     * @param modelClass a {@code Class} whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
```

`ViewModelProvider.Factory`は`ViewModel`の**生成**に関わっています。
`Factory`を使わなくても例えば普通に`MainViewModel()`を呼んだら生成できますが、`Factory`を通すことで *生成する/しない* ・ *生成のタイミング* を`ViewModelProvider`に移譲します。

# ViewModelProvider

```java
public class ViewModelProvider {
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        // mViewModelStoreに取得しようとしているViewModelがあればそのまま返す
        // なければmFactoryを使ってViewModelを生成した上で、mViewModelStoreに入れてあとに返す
    }
}
```

`ViewModelProvider`は大体デザインパターンでいうとBuilderパターンのDirectorの役割です。`Factory`と`ViewModelStoreOwner`を**制御**して`ViewModel`を返します。

`Factory`を使って`ViewModel`を生成し、`ViewModelStore`に入れる作業をするし、`ViewModel`がすでに`ViewModelStore`に入っていたら生成しないで、直接`ViewModel`を返します。



# Activityの`ViewModelStoreOwner`の実装

回転などで再生成されたActivityでも一律前と同じ`ViewModelStore`のインスタンスを返すことで、Activityのライフサイクルを超えて`ViewModel`は生存できました。

ではActivityはどうやって同じ`ViewModelStore`を返すのか、これはComponentActivity[^1]内の`getViewModelStore`の実装を見ればわかります。
結論を先にいうと、`getLastNonConfigurationInstance`と`onRetainNonConfigurationInstance`を使って実現してました。

```java
class ComponentActivity extends androidx.core.app.ComponentActivity implements ViewModelStoreOwner, ... {
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}
```

mViewModelStoreがまだ初期化されていなければ、`getLastNonConfigurationInstance`から`ViewModelStore`を取得します。

この`getLastNonConfigurationInstance`はあまり馴染みがないので、調べてみました。どうもViewModelより以前からある仕組みです。

```java
public Object getLastNonConfigurationInstance()
public Object onRetainNonConfigurationInstance()
```

Activityがdestroyされる前に`onRetainNonConfigurationInstance`は呼ばれます。この関数をオーバーライドして任意のオブジェクトを返せば、次にActivityが再生成されたときに`getLastNonConfigurationInstance`から完全同一オブジェクトを取得できます（そのオブジェクトは死なないです）。

ComponentActivityはこの仕組を利用して、Activityがdestroyされる前に`ViewModelStore`を`onRetainNonConfigurationInstance`で一旦退避させて、Activityが再生成されて`getViewModelStore`が呼ばれたときに**同じオブジェクトを返す**ようにしました。

この`getLastNonConfigurationInstance`と`onRetainNonConfigurationInstance`は、今では使われない機能らしいですが、昔はそれなりに活躍していたらしいです。だが今はComponentActivity内では`onRetainNonConfigurationInstance`を`final`にしているので、オーバーライドさせてもらえないです。
（代わりに名前が似ているド同等機能の`onRetainCustomNonConfigurationInstance`と`getLastCustomNonConfigurationInstance`がありますが、この2つは`final`になってないのでオーバーライドできますが、Deprecatedな関数なので、あえて使う必要がないでしょう。）

# 結論

`ViewModel`の**生成**は`Factory`の責務で、ViewModelの**保存・生存期間**は`ViewModelStoreOwner`(`ViewModelStore`)の責務です。

Activity間で`ViewModel`を共有したければ、共通の`ViewModelStore`を持てばいいです。
その`ViewModelStore`はせめてActivityより長生きしないとだめなので、Activityより長いライフサイクルを持つところにその`ViewModelStore`を置くしかないです。

つまり手取り早く`Application`に置くことになりそうです。（もしくはDaggerで`ViewModelStore`のシングルトンを注入します）

[^1]: ComponentActivity https://android.googlesource.com/platform/frameworks/support/+/8514bc0f4d930b5470435aa365719b2a6a3ad2f3/activity/src/main/java/androidx/activity/ComponentActivity.java#217