Hilt環境でのDefaultViewModelProviderFactoryの実装

# ３行まとめ
- `@DefaultActivityViewModelFactory` および`@DefaultFragmentViewModelFactory` のQualifierを利用してマルチバインディングすると、ViewModelProvider.Factoryを指定しない形でViewModelを生成した際に使用される `DefaultViewModelProviderFactory`の実装が可能である。
- `@HiltViewModel` をViewModelに付与すると、ViewModelのパッケージ名をKeyとした`Map<String, ViewModel>型`のオブジェクトグラフが登録される。
- `HiltViewModelFactory`では、`@HiltViewModel`が付いたViewModelに対しては、内部で定義された`HiltViewModelFactory`が使用され、それ以外のViewModelに対しては、`今回指定するViewModelProviderFactory `か` SavedStateViewModelFactory() `が使用される。

# 検証環境
```
com.google.dagger:hilt-android:2.32-alpha"
```

# 実装Sample
https://github.com/AkitoshiHashizume/HiltViewModelSample

# はじめに
### Hilt環境でのDefaultViewModelProviderFactory
Hiltでは、Activity/Fragmentに`@AndroidEntryPoint`を追記すると、AppCompatActivity/Fragmentと該当のActivity/Fragmentクラスの間に、`Hilt_〇〇Activity/Fragment`が自動生成される。

```UserActivity.kt
@AndroidEntryPoint
class UserActivity : AppCompatActivity() {

  private val userViewModel: UserViewModel by viewModels()
  private val sampleViewModel: SampleViewModel by viewModels()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    userViewModel.login()
    sampleViewModel.showId()
  }
}

// ↓↓↓　自動生成された中間ファイル

public abstract class Hilt_UserActivity extends AppCompatActivity implements GeneratedComponentManagerHolder {
// 中略
}
```

そして、この中間ファイルは`HasDefaultViewModelProviderFactory.java`で定義されている`getDefaultViewModelProviderFactory` をOverrideしており、ViewModelProvider.Factoryを指定しない形でViewModelを生成した際に使用される`defaultViewModelProviderFactory`を、Hiltライブラリ内で実装されている`HiltViewModelFactory` に差し替えている。

```Hilt_UserActivity.kt
// ↓↓↓　自動生成された中間ファイル
public abstract class Hilt_UserActivity extends AppCompatActivity implements GeneratedComponentManagerHolder {
  private volatile ActivityComponentManager componentManager;
// 中略
  @Override
  public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    return DefaultViewModelFactories.getActivityFactory(this); // HiltViewModelFactoryの作成
  }
}
```

これにより、ViewModelに対して`@HiltViewModel`を付与するだけでViewModelに対するInjectをHilt経由で行えるのだが、この恩恵を受ける事ができる一方で、Hiltの中間ファイルが`getDefaultViewModelProviderFactory` をOverrideしてしまっている為、`@AndroidEntryPoint`を追記する以前には可能であった`DefaultViewModelProviderFactory`のカスタマイズの両方を行う事ができなくなっている。
※もちろんActivity/Fragment側で、更に`getDefaultViewModelProviderFactory` をOverrideする事でカスタマイズは可能だが、その場合、ViewModelへのHilt経由でのInjectを、Hiltライブラリ側に任せる事が出来なくなる。

```UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userManager: UserManager
) : ViewModel() 
```

そこで本記事では、Hilt側で用意されている`@DefaultActivityViewModelFactory` および`@DefaultFragmentViewModelFactory` の両Qualifierを利用して、下記の両方を実現する方法を記載する。

- `@HiltViewModel`のついたViewModelへのHilt経由でのInject
- `@AndroidEntryPoint`を追記する以前には可能であった(`@HiltViewModel`の付かないViewModelに対する)`DefaultViewModelProviderFactory`のカスタマイズ

# 実現方法
時間の無い方の為に先に結論を記載しておくと、Hiltの`DefaultViewModelProviderFactory`では、[Daggerのマルチバインディング機能](https://dagger.dev/dev-guide/multibindings.html "Daggerのマルチバインディング機能")を使用している為、以下の様に`ViewModelProviderModule`を定義する事で、`DefaultViewModelProviderFactory`の指定が可能となる。


### ActivityへのDefaultViewModelProviderFactoryの実装

```ViewModelProviderModule.kt
@Module
@InstallIn(ActivityComponent::class)
object ViewModelProviderModule {

    @Provides
    @IntoSet
    @DefaultActivityViewModelFactory
    @Suppress("UNCHECKED_CAST")
    fun provideDefaultActivityViewModelFactory(): ViewModelProvider.Factory {
        return object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                // ここでViewModel生成のカスタマイズを行う。
            }
        }
    }
}
```

### FragmentへのDefaultViewModelProviderFactoryの実装

```ViewModelProviderModule.kt
@Module
@InstallIn(ActivityComponent::class) // ActivityComponentに配置する点だけ注意
object ViewModelProviderModule {

    @Provides
    @IntoSet
    @DefaultFragmentViewModelFactory
    @Suppress("UNCHECKED_CAST")
    fun provideDefaultFragmentViewModelFactory(): ViewModelProvider.Factory {
        return object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                // ここでViewModel生成のカスタマイズを行う。
        }
    }
```

なおFragmentの方のコメントにも記載しているが、Hiltライブラリ内の`DefaultViewModelFactories. ActivityModule `においては、`ActivityComponent`に対して、`@DefaultActivityViewModelFactory` と`@DefaultFragmentViewModelFactory`のマルチバインディングが登録されているので、Fragmentへの`DefaultViewModelProviderFactory`も`ActivityComponent`に配置する。

```DefaultViewModelFactories.java
/** The activity module to declare the optional factories. */
  @Module
  @InstallIn(ActivityComponent.class)
  interface ActivityModule {
    @Multibinds
    @HiltViewModelMap.KeySet
    abstract Set<String> viewModelKeys();

    @Multibinds
    @DefaultActivityViewModelFactory
    Set<ViewModelProvider.Factory> defaultActivityViewModelFactory();

    @Multibinds
    @DefaultFragmentViewModelFactory
    Set<ViewModelProvider.Factory> defaultFragmentViewModelFactory();
  }
```
# DefaultViewModelFactoriesの実装詳細
### なぜ、上記`ViewModelProviderModule`の定義だけで実現可能なのか？
ここからは、中間ファイルによって生成される`getDefaultViewModelProviderFactory()`の実装詳細にも触れつつ、Hiltライブラリ内のHiltViewModelFactoryの実装を紹介していく。
(少し長くなるがお付き合いいただきたい)

### HiltViewModelFactoryの取得

以降では、@AndroidEntryPointのついたActivityの場合を例として、中間ファイルがOverrideした以下のメソッド内で使用されている`DefaultViewModelFactories.java`の内容を追っていく。


```Hilt_UserActivity.kt
  @Override
  public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    return DefaultViewModelFactories.getActivityFactory(this);
  }
```
まず`getActivityFactory()` の実装内容を覗いてみると、以下の様に`ActivityEntryPoint`経由で、Activityから`InternalFactoryFactory`を取得し、`InternalFactoryFactory#fromActivity()`を実行する事で`HiltViewModelFactory` を生成している。
※このInternalFactoryFactoryの実装が、`HiltViewModelFactory`作成の要所となるので、詳細に触れていく。

```DefaultViewModelFactories.java
public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
    return EntryPoints.get(activity, ActivityEntryPoint.class)
        .getHiltInternalFactoryFactory() // InternalFactoryFactoryの取得
        .fromActivity(activity); //HiltViewModelFactory(=ViewModelProvider.Factory)の生成
  }
// (中略)

  @EntryPoint
  @InstallIn(ActivityComponent.class)
  public interface ActivityEntryPoint {
    InternalFactoryFactory getHiltInternalFactoryFactory();
  }
```
```DefaultViewModelFactories.java
/** Internal factory for the Hilt ViewModel Factory. */
  public static final class InternalFactoryFactory {
// (中略)
    @Nullable private final ViewModelProvider.Factory defaultActivityFactory;
// (中略)
    ViewModelProvider.Factory fromActivity(ComponentActivity activity) {
      return getHiltViewModelFactory(activity,
          activity.getIntent() != null ? activity.getIntent().getExtras() : null,
          defaultActivityFactory);
    }
}
```

まず、上記コード内の以下の部分だが、

```DefaultViewModelFactories.java
public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
    return EntryPoints.get(activity, ActivityEntryPoint.class)
        .getHiltInternalFactoryFactory() // InternalFactoryFactoryの取得
// (中略)
}
```
`EntryPoints.get`の処理内容の実態は、第一引数に指定されたActivity/Fragmentなどから、自動生成された中間ファイルの所持する`ComponentManager`を取得し、その`ComponentManager` の保持する`Component`を、第二引数で指定された`ActivityEntryPoint`に型変換する事である。
つまり、自動生成される`ActivityComponent`から`InternalFactoryFactory`を取得する処理が実行されている。

```EntryPoints.java
/** Static utility methods for accessing objects through entry points. */
public final class EntryPoints {
  @Nonnull
  public static <T> T get(Object component, Class<T> entryPoint) {
    if (component instanceof GeneratedComponent) {
      return entryPoint.cast(component);
    } else if (component instanceof GeneratedComponentManager) {
      return entryPoint.cast(((GeneratedComponentManager<?>) component).generatedComponent());
    } else {
      throw new IllegalStateException();
    }
  }
}
```

### EntryPoint経由での`InternalFactoryFactory`の取得を深堀してみる
流石に、`ActivityComponent`から`InternalFactoryFactory`を取得する部分は、実例が無いと分かりにくいので、インターフェースである`ActivityEntryPoint` を具体化している`ActivityCImpl(Hiltの自動生成ファイル)`の中身を覗いてみる。

```DaggerApp_HiltComponents_SingletonC.java
private final class ActivityCImpl extends App_HiltComponents.ActivityC {
      @Override
      public DefaultViewModelFactories.InternalFactoryFactory getHiltInternalFactoryFactory() {
        return DefaultViewModelFactories_InternalFactoryFactory_Factory.newInstance(
                ApplicationContextModule_ProvideApplicationFactory.provideApplication(DaggerApp_HiltComponents_SingletonC.this.applicationContextModule),
                keySetSetOfString(),
                new ViewModelCBuilder(),
                defaultActivityViewModelFactorySetOfViewModelProviderFactory(),
                defaultFragmentViewModelFactorySetOfViewModelProviderFactory()
        );
      }
}
```
ここで、`DefaultViewModelFactories_InternalFactoryFactory_Factory.newInstance`という部分では、Daggerから特定のインスタンスを取得する際に使用される自動生成のメソッドを呼び出しており、その実態は、以下で定義されている`DefaultViewModelFactories#InternalFactoryFactory`のコンストラクタを、Daggerに登録されているオブジェクトグラフの情報を元に呼び出している処理となる。
※Daggerの処理に関しては本記事のスコープ外な為、詳細は割愛するが、要はDaggerの登録情報を元に`DefaultViewModelFactories#InternalFactoryFactory`をインスタンス化しているだけである。

```DefaultViewModelFactories.java
/** Internal factory for the Hilt ViewModel Factory. */
  public static final class InternalFactoryFactory {

    private final Application application;
    private final Set<String> keySet;
    private final ViewModelComponentBuilder viewModelComponentBuilder;
    @Nullable private final ViewModelProvider.Factory defaultActivityFactory;
    @Nullable private final ViewModelProvider.Factory defaultFragmentFactory;

    @Inject
    InternalFactoryFactory(
            Application application,
        @HiltViewModelMap.KeySet Set<String> keySet,
        ViewModelComponentBuilder viewModelComponentBuilder,
        // These default factory bindings are temporary for the transition of deprecating
        // the Hilt ViewModel extension for the built-in support
        @DefaultActivityViewModelFactory Set<ViewModelProvider.Factory> defaultActivityFactorySet,
        @DefaultFragmentViewModelFactory Set<ViewModelProvider.Factory> defaultFragmentFactorySet) {
      this.application = application;
      this.keySet = keySet;
      this.viewModelComponentBuilder = viewModelComponentBuilder;
      this.defaultActivityFactory = getFactoryFromSet(defaultActivityFactorySet);
      this.defaultFragmentFactory = getFactoryFromSet(defaultFragmentFactorySet);
    }
}
```
そして、`InternalFactoryFactory` のコンストラクタInjectionの定義と、自動生成された`ActivityCImpl`の両方をみてみると、`InternalFactoryFactory` のインスタンスは

|引数|引数名：型|指定される情報|
|:-----------|------------:|:------------:|
| 第1引数|application : Application|Hiltが配布するApplicationContextをApplication型にキャストして指定|
|第2引数|`@HiltViewModelMap.KeySet` keySet : Set<String>|`@HiltViewModel`が付いたViewModelのパケージ名のSetが指定される(後述で補足)|
|第3引数|viewModelComponentBuilder : ViewModelComponentBuilder |DefaultFragmentViewModelFactoryが要求される際に、`new ViewModelCBuilder()` で新規インスタンスが生成される|
|第4引数|`@DefaultActivityViewModelFactory` defaultActivityFactorySet : Set<ViewModelProvider.Factory> |[実装方法](https://qiita.com/AkitoshiHashizume/private/44a0bc62235ba5e68d6a#%E5%AE%9F%E7%8F%BE%E6%96%B9%E6%B3%95)で、マルチバインディングによって指定したViewModelProvider.FactoryのSet|
|第5引数|`@DefaultFragmentViewModelFactory` defaultFragmentFactorySet : Set<ViewModelProvider.Factory>|(上記に同じく)|


という情報を元にインスタンス化される。

### (補足) `@HiltViewModel`を付与した際の自動生成ファイル
第3引数に関する補足だが、Hiltでは、下記の様にViewModelに `@HiltViewModel`を付与すると、

```UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userManager: UserManager
) : ViewModel()
```
ViewModelのパッケージ情報から、`@HiltViewModelMap.KeySet` というQualifierでパケージ名の文字列がマルチバインディングされ、ViewModel本体も、パッケージ名をStringKeyとして`@HiltViewModelMap`というQualifierで`Map<String, ViewModel>` の形でオブジェクトグラフに登録される。

```UserViewModel_HiltModules.kt
  @Module
  @InstallIn(ViewModelComponent.class)
  public abstract static class BindsModule {
    @Binds
    @IntoMap
    @StringKey("com.akitoshi.hashizume.hiltviewmodelsample.viewmodel.UserViewModel")
    @HiltViewModelMap
    public abstract ViewModel binds(UserViewModel vm);
  }

  @Module
  @InstallIn(ActivityRetainedComponent.class)
  public static final class KeyModule {
    private KeyModule() {
    }

    @Provides
    @IntoSet
    @HiltViewModelMap.KeySet
    public static String provide() {
      return "com.akitoshi.hashizume.hiltviewmodelsample.viewmodel.UserViewModel";
    }
  }
```
### HiltViewModelFactoryのインスタンス作成
山場である`InternalFactoryFactory`の取得までを紹介できたので、最後に、HiltViewModelFactory作成まで一気に追っていく。

```DefaultViewModelFactories.java
public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
    return EntryPoints.get(activity, ActivityEntryPoint.class)
        .getHiltInternalFactoryFactory() // InternalFactoryFactoryの取得
        .fromActivity(activity); //ViewModelProvider.Factoryの生成
  }
```
`DefaultViewModelFactories#getActivityFactory()` では、InternalFactoryFactoryの取得をした後に`InternalFactoryFactory#fromActivity()` を実行しているが、`InternalFactoryFactory#fromActivity()` の実装は以下の様になっており、

```DefaultViewModelFactories.java
/** Internal factory for the Hilt ViewModel Factory. */
  public static final class InternalFactoryFactory {

    private final Application application;  // 上の表で示したインスタンス作成時の第一引数
    private final Set<String> keySet;  // 第二引数
    private final ViewModelComponentBuilder viewModelComponentBuilder; // 第三引数
    @Nullable private final ViewModelProvider.Factory defaultActivityFactory; // 第四引数
    @Nullable private final ViewModelProvider.Factory defaultFragmentFactory; // 第五引数
// (中略)
    ViewModelProvider.Factory fromActivity(ComponentActivity activity) {
      return getHiltViewModelFactory(activity,
          activity.getIntent() != null ? activity.getIntent().getExtras() : null,
          defaultActivityFactory);
    }
// (中略)
    private ViewModelProvider.Factory getHiltViewModelFactory(
        SavedStateRegistryOwner owner,
        @Nullable Bundle defaultArgs,
        @Nullable ViewModelProvider.Factory extensionDelegate) {
      ViewModelProvider.Factory delegate = extensionDelegate == null
          ? new SavedStateViewModelFactory(application, owner, defaultArgs)
          : extensionDelegate;
      return new HiltViewModelFactory(
          owner, defaultArgs, keySet, delegate, viewModelComponentBuilder);
    }
```
最終的には、以下の情報を元に、`HiltViewModelFactory`のインスタンスが作成される。

|引数|引数名：型|指定される情報|
|:-----------|------------:|:------------:|
|第1引数|owner|Activity/FragmentなどのSavedStateRegistryOwner|
|第2引数|defaultArgs|Activity/Fragmentから取得したBundle||
|第3引数|keySet|`@HiltViewModel`が付いたViewModelのパケージ名のSetが指定される|
|第4引数|delegate |[実装方法](https://qiita.com/AkitoshiHashizume/private/44a0bc62235ba5e68d6a#%E5%AE%9F%E7%8F%BE%E6%96%B9%E6%B3%95)で、マルチバインディングによって指定したViewModelProvider.FactoryのSetの「最初」の要素|
|第5引数|viewModelComponentBuilder| `ViewModelCBuilder()` の新規インスタンス|

### `HiltViewModelFactory`の実装内容
次に、`HiltViewModelFactory`の実装内容を追っていく。
まず、`HiltViewModelFactory`コンストラクタの実装は以下の様になっており、引数の情報から、`hiltViewModelFactory`の作成が行われる。

```HiltViewModelFactory.kt
public HiltViewModelFactory(
      @NonNull SavedStateRegistryOwner owner,  // 上の表で示したインスタンス作成時の第一引数
      @Nullable Bundle defaultArgs,  // 第二引数
      @NonNull Set<String> hiltViewModelKeys, // 第三引数
      @NonNull ViewModelProvider.Factory delegateFactory, // 第四引数
      @NonNull ViewModelComponentBuilder viewModelComponentBuilder // 第五引数　
) {
    this.hiltViewModelKeys = hiltViewModelKeys;
    this.delegateFactory = delegateFactory;
    this.hiltViewModelFactory =
        new AbstractSavedStateViewModelFactory(owner, defaultArgs) {
          @NonNull
          @Override
          @SuppressWarnings("unchecked")
          protected <T extends ViewModel> T create(
              @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle) {
            ViewModelComponent component =
                viewModelComponentBuilder.savedStateHandle(handle).build();
            Provider<? extends ViewModel> provider =
                EntryPoints.get(component, ViewModelFactoriesEntryPoint.class)
                    .getHiltViewModelMap()
                    .get(modelClass.getName());
            if (provider == null) {
              throw new IllegalStateException(
                  "Expected the @HiltViewModel-annotated class '"
                      + modelClass.getName()
                      + "' to be available in the multi-binding of "
                      + "@HiltViewModelMap but none was found.");
            }
            return (T) provider.get();
          }
        };
  }
```

そして、肝心の`HiltViewModelFactory`が実装している`ViewModelProvider.Factory`がどうなっているかを確認すると、以下の様になっている。

````HiltViewModelFactory.kt
@NonNull
  @Override
  public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    // hiltViewModelKeysは、`@HiltViewModel`が付いたViewModelのパケージ名のSet
    if (hiltViewModelKeys.contains(modelClass.getName())) {
      return hiltViewModelFactory.create(modelClass);
    } else {
      //delegateFactoryは、今回マルチバインディングによって指定したViewModelProvider.Factory
      return delegateFactory.create(modelClass);
    }
  }
```
つまり、作成されようとしているViewModelのクラス名が、`@HiltViewModel`が付いたViewModelのパケージ名のSetに含まれる場合は、HiltによるInjectが要求されているとして、コンストラクタで作成した`hiltViewModelFactory`経由でViewModelの作成を行い、含まれなかった場合は、[実装方法](https://qiita.com/AkitoshiHashizume/private/44a0bc62235ba5e68d6a#%E5%AE%9F%E7%8F%BE%E6%96%B9%E6%B3%95)で、マルチバインディングによって指定したViewModelProvider.Factoryが使われる事となる。

※長くなったが、これで、下記の`ViewModelProviderModule`を定義する事で、DefaultViewModelProviderFactoryの実装の実装が可能な事が確認された。

```ViewModelProviderModule.kt
@Module
@InstallIn(ActivityComponent::class)
object ViewModelProviderModule {

    @Provides
    @IntoSet
    @DefaultActivityViewModelFactory
    @Suppress("UNCHECKED_CAST")
    fun provideDefaultActivityViewModelFactory(): ViewModelProvider.Factory {
        return object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                // ここでViewModel生成のカスタマイズを行う。
            }
        }
    }
}
```

#　注意

##### Hilt経由でInjectされるViewModelに対するDefaultViewModelProviderFactoryの実装
[Hilt環境でのDefaultViewModelProviderFactory](https://qiita.com/AkitoshiHashizume/private/44a0bc62235ba5e68d6a#hilt%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AEdefaultviewmodelproviderfactory)の最後に示した様に、@DefaultActivityViewModelFactory / @DefaultFragmentViewModelFactoryは、@AndroidEntryPointをつける以前に実装可能であった`HasDefaultViewModelProviderFactory. getDefaultViewModelProviderFactory()` を置き換えるものであり、`HiltViewModelFactory`によって生成されるViewModelの生成に対する共通処理を定義できるものでは無い。
その為、@HiltViewModelが付いたViewModelに対しては、`ComponentActivity.viewModels()`や`Fragment.viewModels()` に対するExtensionを定義するなどして、共通処理を定義する事が必要となる。

##### @DefaultActivityViewModelFactoryを使用しなかった場合の@HiltViewModelの付かないViewModelに対するインスタンス化について

本筋とは外れるので、[HiltViewModelFactoryのインスタンス作成](https://qiita.com/AkitoshiHashizume/private/44a0bc62235ba5e68d6a#hiltviewmodelfactory%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E4%BD%9C%E6%88%90)では触れなかったが、`InternalFactoryFactory.getHiltViewModelFactory()`内の処理をみると、マルチバインディングによって`ViewModelProvider.Factory`を指定しない場合は、`SavedStateViewModelFactory`のインスタンスが使用されている。
※つまり引数無しのコンストラクタによってインスタンス化される。

その為、引数を要求するViewModelなどをインスタンス化するときには、この記事で紹介した@DefaultActivityViewModelFactoryや@DefaultFragmentViewModelFactoryを指定する必要がある。

```DefaultViewModelFactories.java
//extensionDelegateは、マルチバインディングによって指定したViewModelProvider.FactoryのSet要素 or 指定が無い場合はnull
//変数delegateは、HiltViewModelFactoryで使用される@HiltViewModelの付かないViewModelに対するViewModelFactory
ViewModelProvider.Factory delegate = extensionDelegate == null
          ? new SavedStateViewModelFactory(application, owner, defaultArgs)
          : extensionDelegate;
```