【Android】Hiltを導入する

# はじめに
この記事では、Android開発において用いられる依存性注入ライブラリのHiltについて自分の理解をまとめておきます。
初学者ですので、記事の内容には誤りが含まれる可能性がありますがコメントで指摘していただけると幸いです。

Hiltの公式ドキュメントはこちら↓
https://developer.android.com/training/dependency-injection/hilt-android?hl=ja
# 依存性注入(DI)の概要
依存性注入(Dependency Injection)とは、クラスが必要とするオブジェクトを外部から注入することでコードの柔軟性を向上させる手法です。
DIのメリットとして、**コードが変更に強くなる**という点が挙げられます。(この点については後ほど説明します。)
## 依存性の例
例として、`UserViewModel`というViewModelクラスが`UserRepository`というリポジトリクラスを利用してユーザー情報を取得するというケースを仮定します。
```Kotlin
class UserRepositoryImpl {
    fun getUserName(): String {
        return "UserName"
    }
}
```
```Kotlin
class UserViewModel : ViewModel() {
    // 直接UserRepositoryに依存している
    private val userRepositoryImpl = UserRepositoryImpl()

    fun fetchUserName(): String {
        return userRepositoryImpl.getUserName()
    }
}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796037/6b3ff868-09f8-475b-b943-028efec25291.png
" alt="直接依存がある状態のクラス図">
このコードにおいては、`UserViewModel`の中で`UserRepositoryImpl`クラスのインスタンスを生成しています。これが`UserViewModel`クラスが直接的に`UserRepositoryImpl`クラスに依存している状態になります。この場合、`UserRepositoryImpl`クラスが変更されるとき、`UserViewModel`側のコードも修正する必要が出てきます。
具体例としてリポジトリを`RemoteUserRepository`と`LocalUserRepository`に分けたくなったときを考えると、以下のようにUserViewModelを修正する必要があります。
```Kotlin
class UserViewModel : ViewModel() {
    private val userRepositoryImpl = RemoteUserRepository()

    fun fetchUserName(): String {
        return userRepositoryImpl.getUserName()
    }
}
```
ここで、この依存関係を外部から注入するように変更してみます。
まずは`UserRepository`というインタフェースを用意し、ここで宣言されたメソッドを`UserRepositoryImpl`クラスで実装するという形に設計を変更します。
```Kotlin
interface UserRepository{
    fun getUserName(): String
}
```
```Kotlin
class UserRepositoryImpl : UserRepository{
    override fun getUserName(): String {
        return "UserName"
    }
}
```
そして、`UserViewModel`はインタフェースである`UserRepository`に依存し、`UserRepositoryImpl`の具体的な実装には依存しないようにします。
```Kotlin
class UserViewModel(private val userRepository: UserRepository):ViewModel() {
    fun fetchUserName(): String {
        return userRepository.getUserName()
    }
}
```
上記のコードには、上で紹介した`UserViewModel`が`UserRepositoryImpl`に直接依存しているケースと比較して以下のような変更点があります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796037/0b48142d-5b72-4b38-8945-fb6bd47966d7.png" alt="依存関係を注入した状態のクラス図">

## DIのメリット
DIのメリットとしては**実装の変更がしやすくなる**ということが挙げられます。例えば、`UserRepositoryImpl`が以下のような`LocalUserRepository`に変更されたとします。
```Kotlin
class LocalUserRepository : UserRepository {
    override fun getUserName(): String {
        return "LocalUserName"
    }
}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796037/f464ce7f-b641-4947-a758-7f80dc4309d9.png" alt="実装クラス変更された状態のクラス図">

このケースではインタフェースである`LocalUserRepository`を実装するクラスが変更されますが、`UserViewModel`側では`UserRepository`を引数として受け取るままでよく、コードに変更を加える必要性はありません。

# Hiltの導入
それでは実際にHiltを使って依存関係を注入していきます。
まずProjectレベルのbuild.gradleに以下の内容を追加します。
```build.gradle.kts(Project)
plugins {
    ・・・
    id("com.google.dagger.hilt.android") version "2.51.1" apply false
}
```
また、Moduleレベルのbuild.gradleに以下の内容を追加します。
```build.gradle.kts(Module)
plugins {
    id("kotlin-kapt")
    id("com.google.dagger.hilt.android")
}
・・・
dependencies{
    ・・・
    implementation("com.google.dagger:hilt-android:2.51.1")
    kapt("com.google.dagger:hilt-android-compiler:2.51.1")
}
```
次にApplicationクラスに`@HiltAndroidApp`というアノテーションを付与します。
Applicationクラスを作っていない場合は、以下のように空でよいので`Application`クラスを作成します。
```Kotlin
@HiltAndroidApp
class SampleApplication : Application()
```
ここからは上の例で挙げている、「`UserViewModel`クラスが`UserRepository`インタフェースに依存し、その実装を`UserRepositoryImpl`が行っている」という例を用いて説明していきます。
依存関係の注入を行うためには、`UserRepository`のインスタンスを`UserViewModel`に渡すとき、そのUserRepositoryの具体的な実装(どのインスタンスを渡せばよいのか)をHiltに伝える必要があります。

今回はUserRepositoryの依存関係をModule.ktというファイルに記述することにします。
```Module.kt
@Module
@InstallIn(SingletonComponent::class)
object Module{
    @Provides
    @Singleton
    fun provideUserRepository(): UserRepository {
        return UserRepositoryImpl() // 実際の実装クラスを返す
    }
}
```
`@Module`はHilt に依存関係の提供方法を教えるためのクラスであることを示します。
`@InstallIn(singletonComponent::class)`はモジュールがアプリ全体（SingletonComponent）に適用されることを意味します。
また、`@Provides`は`provideUserRepository`関数が具体的な実装方法を提供するメソッドであることを示しています。さらに`@Singleton`は提供される `UserRepository` のインスタンスがアプリ全体で1つだけ存在するようにすることを保証するものです。

次にActivityクラスに`@AndroidEntryPoint`アノテーションを付与します。依存関係の注入を行うには注入を行いたいクラスとその上位のコンポーネントにアノテーションを付与する必要があるためです。
```Kotlin
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    ・・・
}
```

そしてUserViewModelクラスに`@HiltViewModel`アノテーションを付与、さらにコンストラクタに `@Inject` を付与します。
```Kotlin
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {
    ・・・
}
```
これにより、Hilt が UserRepository の依存関係を解決し、UserViewModel に注入することができます。