Dagger-HiltはViewModelのFactoryをどのように差し込むのか

AAkiraさんのブログを見てViewModelのInjectがすごいと思って不思議に思って仕組みを調べてみました。
https://aakira.app/blog/2020/05/dagger-hilt/

まだ正式にリリースされているわけではないので後々仕組みが変わる可能性があります。
このブログで紹介されているサンプルコードをベースに見ていきます。 (理解しやすいサンプルを公開していただき、本当にありがとうございます :pray: )
https://github.com/AAkira/dagger-hilt-example

以下のように書いておくことで、ViewModelをInjectできるそうです。

```kotlin
class MainViewModel @ViewModelInject constructor(
    private val repository: SampleRepository,
    @Assisted private val savedState: SavedStateHandle
) : ViewModel() {
}
```

```kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Log.v("main activity", "activity view model: $viewModel")
    }
}
```

なぜ、これだけでViewModelのDIができるのでしょうか？

## viewModels{}メソッドについて

ここではHiltではなく、元からAndroid Jetpackにある`viewModels<>()`という関数を使っているのですが、viewModels<>()はデフォルトでは**`ComponentActivity#getDefaultViewModelProviderFactory()`を経由してViewModelを生成**して、getViewModelStore()で管理しているところにViewModelを保存します。
つまり、うまく**このProviderFactoryを変えることができれば、ViewModelのinjectができそう**です。

```kotlin
@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        // **ここでComponentActivity#getDefaultViewModelProviderFactory()を使う**
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
```

## どうやって`getDefaultViewModelProviderFactory()`を変えるか？

実際にアプリを動かしてコードを見てみましょう。
MainActivityのsuperクラスはAppCompatActivityのはずでしたが。。。

```kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {
```

**デバッグで確認する以下のように MainActivityのsuperクラスとして、`Hilt_MainActivity`というクラスがいることがわかります**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/4dcc1a53-f53b-8454-def0-7eff960be3af.png)

Hilt_MainActivityは以下のようになっており、 **getDefaultViewModelProviderFactory() が実装されています。**これによってViewModelのProviderFactoryが生成されます。

```kotlin
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
  private volatile ActivityComponentManager componentManager;
...
  @Override
  public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    ViewModelProvider.Factory factory = DefaultViewModelFactories.getActivityFactory(this);
    if (factory != null) {
      return factory;
    }
    return super.getDefaultViewModelProviderFactory();
  }
}
```

また少し脇道にそれますが、Hilt_MainActivityでは面白いことに**この生成されたコードの中でcomponentを持つActivityComponentManagerを生成しています。**


```kotlin
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
  private volatile ActivityComponentManager componentManager;
...
  protected ActivityComponentManager createComponentManager() {
    return new ActivityComponentManager(this);
  }

  protected final ActivityComponentManager componentManager() {
    if (componentManager == null) {
      synchronized (componentManagerLock) {
        if (componentManager == null) {
          componentManager = createComponentManager();
        }
      }
    }
    return componentManager;
  }
```



話を戻しますが、`DefaultViewModelFactories.getActivityFactory(this)`を追ってみましょう。

```kotlin
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
  private volatile ActivityComponentManager componentManager;
...
  @Override
  public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    // ********
    ViewModelProvider.Factory factory = DefaultViewModelFactories.getActivityFactory(this); 
```

生成されたコード内でしか使わないで！というメソッドがあります。 **基本的にはactivityからDaggerのコンポーネントを取得して、そのコンポーネントにあるモジュールからViewModelのProviderFactoryを取得します。**
`EntryPoints.get(activity, ActivityEntryPoint.class)`というのを呼び出すのですが、EntryPointsについては深くは説明しないのですが(インスタンス取得を楽にする面白い仕組みがありそうでした。)、ここではHilt_MainActivityのメソッドで作られているActivityのスコープのComponentを取得して返しています。
その後、`.getActivityViewModelFactory()`で、そのComponentに生えている`getActivityViewModelFactory()`を呼び出すことで、ModuleでProvideされたViewModelのProviderFactoryを取得しています。

```kotlin
public final class DefaultViewModelFactories {

  /**
   * Retrieves the default view model factory for the activity.
   *
   * <p>Do not use except in Hilt generated code!
   */
  @Nullable
  public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
    return getFactoryFromSet(
        EntryPoints.get(activity, ActivityEntryPoint.class).getActivityViewModelFactory());
  }
```

Hiltのライブラリ内にあるProviderFactoryをProvideするModuleのコードです。
ここでProviderFactoryがProvideされます。

```kotlin
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public final class ViewModelFactoryModules {

    /**
     * Hilt Modules for providing the activity level ViewModelFactory
     */
    @Module
    @InstallIn(ActivityComponent.class)
    public abstract static class ActivityModule {

        @NonNull
        @Multibinds
        abstract Map<String, ViewModelAssistedFactory<? extends ViewModel>> viewModelFactoriesMap();

        @Provides
        @IntoSet
        @NonNull
        @DefaultActivityViewModelFactory
        static ViewModelProvider.Factory provideFactory(
                @NonNull Activity activity,
                @NonNull Application application,
                @NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
                        viewModelFactories) {
            // Hilt guarantees concrete activity is a subclass of ComponentActivity.
            SavedStateRegistryOwner owner = (ComponentActivity) activity;
            Bundle defaultArgs = activity.getIntent() != null
                    ? activity.getIntent().getExtras() : null;
            SavedStateViewModelFactory delegate =
                    new SavedStateViewModelFactory(application, owner, defaultArgs);
            return new HiltViewModelFactory(owner, defaultArgs, delegate, viewModelFactories);
```


# まとめ
中身を知らなくても作れるように作られていますが、気になって見てみても読めるコードになっていて面白かったです。ぜひ興味があれば見てみてください。