【Android】はじめてのDagger2

# はじめに

[前回の記事(DI（依存性の注入）とは依存性を注入するということである、、？)](https://qiita.com/iTakahiro/items/353a11f6c9d2a927158d)では
DIとは何かについて記述しました。

今回は[Dagger2](https://dagger.dev)を導入してみたので記事にまとめます。
言語はKotlinです！

参考にしたチュートリアルは[こちら](https://www.raywenderlich.com/6583167-dagger-2-tutorial-for-android-advanced)です。

# 準備

まず、`build.gradle`にて`Dagger2`を導入します。
参照：[Dagger README.md](https://github.com/google/dagger#gradle)

```gradle:build.gradle(app)

apply plugin: 'kotlin-kapt' // Kotlinでアノテーションを使用するために必要

dependencies {
    ..

    // 2020/4/26現在、
    // バージョンによって、使用できるアノテーションの種類が異なる.
    // また、2.25 ではコードが自動生成されない問題があることを確認済み.
    def dagger_version = 2.27 

    implementation "com.google.dagger:dagger:$dagger_version"
    annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"

    // Kotlinで開発する場合は必要.
    // KotlinでもDaggerに関するアノテーションを使用できるようにするためのもの.
    kapt "com.google.dagger:dagger-compiler:$dagger_version" 
}
```

# Daggerの概要

Daggerとは、オブジェクト間の依存関係を管理するコードを**自動生成**してくれるツールです。

Daggerがコードを自動生成するために必要なこと：

1. 提供するオブジェクトを把握する
1. 注入先のオブジェクトを把握する
1. 注入を実行する

# 1. 提供するオブジェクトを把握する

まずは、どのオブジェクトが何に依存しているのかを開発者が把握する必要がある。

## どのオブジェクトを注入するのか

自分が依存関係を把握できたら、次はアノテーションを用いて「Dagger」にそれを教えてあげる

e.g.

```kotlin
@Module
class DatabaseModule {
    @Provides
    fun providePlayCallDao(): PlayCallDao {
        return App.database.playCallDao()
    }
}

```

## `@Provides`

`@Provides`(提供)したいオブジェクトのインスタンス化メソッドにつけることで、何を`@Provides`したいかをDaggerに知らせる。

## `@Module `

`@Provides`するためのメソッドを、`@Module`クラスにまとめる。

他の`@Module`も含めることができる。

e.g.

```kotlin
@Module
class DatabaseModule

@Module(includes = [DatabaseModule::class])
class RepositoryModule
```

## ちょこっとまとめ

提供するオブジェクトをDaggerに知らせることができた！

-> これで、Daggerはコードを自動生成することができる。そして、`@Provides`するオブジェクトの生成方法を覚えてくれる。

# 2. 注入先のオブジェクトを把握する

Daggerは、1.の過程で何を注入するのかを把握することができた。
しかし、`@Provides`されるオブジェクトをどこで`@Inject`するのかは把握できていない。

-> どこで使うためのものなのかを知らせてあげよう！

注入したいタイミングで`@Inject`すれば良いが、後述の Field Injection を用いる場合は`@Component`を作成する必要があります。
ここでは、その`@Component`について説明します。

## `@Component`
`@Component`を定義し、以下の役割を担わせる。
これは後述の Field Injection で使用します。

役割：

- `@Provides`するオブジェクトを解放する
- 目標のクラスに依存性を注入する

含まれるもの：

- 使用する`@Module`の宣言
- inject用メソッドのインターフェース<br>
  このメソッドの引数に injectする対象のクラス(`@Inject`を記述するクラス)の型を設定します。<br>
  ここで定義しておけば、あとは Dagger が処理内容を自動生成してくれます。<br>
  `inject()`の使用方法は[次節](#3-注入を実行する)に持ち越します。

e.g. 

```kotlin
@Component(modules = [DatabaseModule::class])
interface AppComponent {
    // Daoの注入
    fun inject(repository: PlayCallListRepository)
}
```

# 3. 注入を実行する

`@Inject` を用いて実装します。

## `@Inject`

`@Inject`したいオブジェクトを宣言している箇所につけます。
例は後述します。

## Daggerが提供するDIの種類

Daggerが提供するDIの種類は基本的に以下の2つがあります。
＊数字は優先度

1. Constructor Injection
2. Field Injection

優先度の理由と各種詳細を以下に記述します。

### 1. Constructor Injection

e.g.

```kotlin
class PlayCallRepository @Inject constructor(
    private val dao: PlayCallDao
) {
    // このメソッドの実装は Dagger には関係ありません
    suspend fun loadAllPlayCall(): List<PlayCallEntity> {
        return dao.loadAllPlayCall()
    }
}
```

1番良さげな理由:

- オブジェクト生成時に依存関係をまとめて設定できるから<br>(ここでは注入するものが`PlayCalldao`のみですが)
- 実装が手軽だから<br>`@Component`を作成しなくても良い

なので、可能であれば Contructor Injection を適用するのが良いと思います。

ただし、`Activity`や`Fragment`などの Android環境独自の Lifecycleを持つオブジェクトに対しては使えません。
-> Field Injectionを使用する

### 2. Field Injection

e.g.

```kotlin:PlayCallListFragment.kt
class PlayCallListFragment : Fragment() {
    @Inject
    lateinit var viewModel: PlayCallListViewModel
    ..
}
```

```kotlin:PlayCallRepository.kt
class PlayCallRepository {
    @Inject
    lateinit var dao: PlayCallDao
    ..
```

`@Inject`アノテーションをつけるだけだと、Daggerは依存性を注入してくれません
-> [@Component](#component) を作成し、inject用メソッドを用意する必要がある。

e.g.

```kotlin:AppComponent.kt
@Component(modules = [AppModule::class])
interface AppComponent {
  fun inject(repository: PlayCallListRepository)
}
```

ここで定義した`inject(repository:)`を使用し対象オブジェクト(ここでは `PlayCallListRepository`)に依存性を注入します。<br>
このメソッドを呼んだ後に、`@Inject`を使用することができます。
＊ 逆にいうと、`inject(repository:)`より前に`@Inject`を通ってしまうとコンパイルエラーが発生します。

e.g. `PlayCallRepository` にて

```kotlin:PlayCallRepository.kt
class PlayCallRepository {
    @Inject
    lateinit var dao: PlayCallDao
    init {
        DaggerAppComponent.create().inject(this)
    }
}
```

e.g. `PlayCallListFragment` にて

```kotlin:PlayCallListFragment.kt
class PlayCallListFragment : Fragment() {
    @Inject
    lateinit var viewModel: PlayCallListViewModel

    override fun onAttach(context: Context) {
        DaggerPlayCallListComponent.create().inject(this)
    }
}
```

1つ目の例で少し解説します。

1. `DaggerAppComponent.create()`で`DaggerAppComponent`を生成します<br>
1. `AppComponent`内で定義した`inject(repository:)`メソッドを呼ぶことで`@Inject`アノテーションが付いているプロパティに依存性を注入することができます

補足：
`DaggerAppComponent`はDaggerが自動生成したクラスです。
このクラス内で、`AppComponent`で定義したインターフェースの処理を実装しています。
正確に言うと、`DaggerAppComponent`内から別クラスのメソッドを呼ぶ処理を実装しています。
Daggerが自動生成した`DaggerAppComponent`クラスを見てみましょう！

```java:DaggerAppComponent.java
public final class DaggerAppComponent implements AppComponent {
  ..
  @Override
  public void inject(PlayCallRepository repository) {
    // ここで、inject(repository:) を実装している
    injectPlayCallRepository(repository);  // メソッドA
  }

  // メソッドA
  private PlayCallRepository injectPlayCallRepository(PlayCallRepository instance) {
  // 次に添付のクラス内の injectDao() を呼ぶ
  PlayCallRepository_MembersInjector.injectDao(instance, DatabaseModule_ProvidePlayCallDaoFactory.providePlayCallDao(databaseModule));
    return instance;
  }
  ..
}
```

```java:PlayCallRepository_MembersInjector.java
public final class PlayCallRepository_MembersInjector implements MembersInjector<PlayCallRepository> {
  ..
  @InjectedFieldSignature("io.github.itakahiro.architecturefootball.repository.PlayCallRepository.dao")
  public static void injectDao(PlayCallRepository instance, PlayCallDao dao) {
    // ここで、PlayCallRepository に PlayCallDao を注入している！！
    instance.dao = dao;
  }
}
```

# まとめ

Dagger の基礎的な部分をまとめてみました。
Dagger では、他にも様々な機能・アノテーションが用意されています。
それらを使いこなすのは難しく感じていますが、少しずつ理解していこうと思います。

## サンプルコード

https://github.com/iTakahiro/ArchitectureFootball

# 参考資料

- [Dagger](https://dagger.dev): 公式ガイド
- [Dagger](https://github.com/google/dagger#gradle): リポジトリ
- [Dagger 2 Tutorial For Android: Advanced](https://www.raywenderlich.com/6583167-dagger-2-tutorial-for-android-advanced): チュートリアル