Hiltの使い方

# この記事について
androidでHiltを使う方法をまとめます。

# DI（依存注入）とは？

依存とはあるオブジェクトから他のオブジェクトを参照・使用することを指します。
以下の例ではCarクラスはEngineクラスに依存していることになります。

```kotlin
class Car {
    var engine : Engine = Engine()
    fun drive() {
        engine.start()
    }
}

class Engine {
    fun start() {
        ...
    }
}
```

UMLのクラス図とシーケン図では以下のように表されます。

![uml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77676/3b47a2d9-a884-b4d5-0bd9-a4bb6747fa36.png)

![uml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77676/ba14ee7f-cdbf-5630-383b-48dfa31ee372.png)


DI(依存注入)とはあるオブジェクトから他のオブジェクトを参照・使用する関係（依存）を注入（あるオブジェクトと他のオブジェクトをつなげる）ことを指します。
Hiltでは以下の2種のDIをサポートしています。
1. Constructor Injection
2. Field Injection

## Constructor Injection
コンストラクタを介してオブジェクトを渡すことで依存関係を注入します。

```kotlin
class Car constructor(private var engine : Engine){
    fun drive() {
        engine.start()
    }
}

class Engine {
    fun start() {
        ...
    }
}
```

この例をHiltを使ってDIを行うと以下のようになります。

```kotlin
class Car @Inject constructor(private var engine : Engine){
    fun drive() {
        engine.start()
    }
}


class Engine {
    fun start() {
        ...
    }
}
```

## Field Injection
先述した例はField Injectionにあたります。

```kotlin
class Car {
    var engine : Engine = Engine()
    fun drive() {
        engine.start()
    }
}

class Engine {
    fun start() {
        ...
    }
}
```

Hiltを使って記述すると以下のようになります。

```kotlin
class Car {
    @Inject lateinit var engine : Engine
    fun drive() {
        engine.start()
    }
}

class Engine {
    fun start() {
        ...
    }
}
```

それではHiltを使って行きましょう。

# Gradleの設定

GradleにHiltをビルドを含めるように設定します。
アプリのbuild.gradleファイルに以下を追加します。

```gradle
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {
    ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
    ...
    implementation "com.google.dagger:hilt-android:2.28-alpha"
    kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
```

次にプロジェクトのbuild.gradleに以下を追加します。

```gradle
buildscript {
    ...
    ext.hilt_version = '2.28-alpha'
    dependencies {
        ...
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }
}
```

# ApplicationクラスにHiltを導入
Applicationクラスに`@HiltAndroidApp`アノテーションを追加します。
Applicationクラスは自分で追加する必要があります。

```kotlin
@HiltAndroidApp
class MyApplication : Application() {
    ...
}
```

アプリのアプリケーションクラスがこれにより変更されるのでAndroid Manifesstファイルにそのことを記述する必要があります。

```xml:
android:name=".MyApplication"
```

これによりHiltのコード自動生成が有効になります。

# アクティビティに具象クラスをDIする
CarクラスをMainActivityアクティビティクラスにDIする場合を考えます。
まずHiltモジュールを作成します。HiltモジュールはDIするクラスの

1. 生成方法
2. ライフタイム

を管理します。

プロジェクトに新たにdiパッケージを追加してください。
diパッケージにCarModule.ktを追加し、以下のように記述するとアプリケーションの寿命を持ったEngineを生成するHiltモジュールになります。

```kotlin
package com.example.android.xxxxx.di
...

@InstallIn(SingletonComponent::class)
@Module
object CarModule {
    @Provides
    fun provideCar() : Car {
        return Car()
    }
}
```

`@Module`はクラスがHiltモジュールで有ることを指定します。

`@InstallIn(SingletonComponent::class)`はインスタンスの寿命を指定しています。
`SingletonComponent::class`はアプリケーション全体が寿命であることを意味しています。
`@InstallIn()`の引数を変えることで寿命を細かく変えることができます。

| Component | Injector for |
|-----------|--------------|
| SingletonComponent | Application |
| ActivityRetainedComponent | ViewModel (see View model extension) |
| ActivityComponent | Activity |
| FragmentComponent | Fragment |
| ViewComponent | View |
| ViewWithFragmentComponent | View with @WithFragmentBindings |
|ServiceComponent | Service |

`@Provides`はインスタンスを生成するメソッドにつけます。メソッド内でインスタンスの生成方法を指定します。
それでは、`MainActivity`に`Car`クラスを注入します。

```kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
{
    @Inject lateinit var car : Car
    ...
}
```

`AndroidEntryPoint`は依存注入をする対象のクラスを指定します。

1. Activity
2. Fragment
3. View
4. Service
5. BroadcastReceiver1

`@Inject`は依存注入するメンバをしていします。Hiltは型を頼りにModule内の`@Provides`で修飾されたメソッドを呼び出し、Carクラスをインスタンス化します。

ちなみに、上のように書くと依存注入が行われるたびに新しいインスタンスが生成されます。同じインスタンスを使いまわしたい場合は`@Singleton`アノテーションをHiltモジュールに指定します。

```kotlin
package com.example.android.xxxxx.di

@InstallIn(SingletonComponent::class)
@Module
object CarModule {
    @Provides
    @Singleton
    fun provideCar() : Car {
        return Car()
    }
}
```

上記の方法はField Injectionで依存注入していますが、以下のようにConstructor Injectionで実装することもできます。

```kotlin
@AndroidEntryPoint
class MainActivity @Inject constructor(var car : Car) : AppCompatActivity()
{
    ...

}
```

# アクティビティにインターフェースをDIする
以下のようなEngineインターフェースとGasolineEngineクラスとElectricEngineクラスを想定します。

```kotlin
interface Engine
{
    fun start()
    fun stop()
}

class GasolineEngine : Engine
{
    override fun start()
    {
        ...
    }
    override fun stop()
    {
        ...
    }
}

class ElectricEngine : Engine
{
    override fun start()
    {
        ...
    }
    override fun stop()
    {
        ...
    }
}
```

UMLのクラス図で上記を表すと以下のようになります。

![uml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77676/2313b030-880d-1e30-8b39-ac94a82c2afa.png)



インターフェースにはabstractクラスのHiltモジュールを用意します。

```kotlin
@Qualifier
annotation class GasolineEngine

@Qualifier
annotation class ElectricEngine

@InstallIn(SigletonComponent::class)
@Module
abstract class EngineModule {
    @GasolineEngine
    @Binds
    abstract fun bindGasolineEngine(impl : GasolineEngine) : Engine

    @ElectriceEngine
    @Binds
    abstract fun bindElectricEngine(impl : ElectricEngine) : Engine
}

```

`@Qualifier`はどのクラスをインスタンス化するかを指定します。
`@Binds`はインスタンス化したい型を引数とし、返り値をインターフェースとしたabstractメソッドに付与します。

上記のモジュールは以下のようにMainActivityにDIします。

```kotlin
@AndroidEntryPoint
class MainActivity constructor() : AppCompatActivity()
{
    @GasolineEngine
    @Inject lateinit var main_engine : Engine

    @ElectricEngine
    @Inject lateinit var sub_engine : Engine
    ...

}
```


# 参考文献

* [Google Code Lab : Using Hilt in your Android app](https://codelabs.developers.google.com/codelabs/android-hilt/#0)
* [Android Developers Documentation : Dependency injection with Hilt](https://developer.android.com/training/dependency-injection/hilt-android)
* [Hilt Documentation](https://dagger.dev/hilt/)