【DI】Dagger2+Retrofit2(+OkHttp3)+ViewModelのDIの最小構成[その1]


# はじめに
DIでDagger2を使っていると、いつも「おんやあ？」となるので自分用にメモっておきます。

Dagger2の構成は初心者にはハードルが高いです。
しかも`Retrofit2`をDIでInjectしようとしたり、引数付きの`ViewModel`を作ろうとするとますます混乱します。
そこで`Dagger2`+`Retrofit2(+OkHttp3)`+`ViewModel`(+レイヤードアーキテクチャ)という、よく使う組み合わせのDIの最小構成（たぶん）をこちらに記載しておきます。

※当方DI初心者ですのでおかしいところがあったらガンガン指摘ください。喜びます（マゾ）。

**2020/03/17** 構成を修正しました。

# リポジトリ
https://github.com/nanaten/DaggerRetrofitViewModel
こちらに全体のソースコードを置いておきます。
DIが機能している事を示すために、GitHubのAPI(repos/octocat/Hello-World)にアクセスして結果をトースト表示してます。
※APIへのアクセスに`RxJava`も使っていますがそちらは解説しない予定です。

# 構成
下記のような構成を想定しています。
![スクリーンショット 2019-12-24 17.33.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309569/aead50d6-606a-e663-4cbd-8d90b42acd76.png)

# 実装
## 1. Gradle設定
```groovy:build.gradle
    final DAGGER_VERSION = '2.25.3'
    final RETROFIT_VERSION = '2.7.0'

    // retrofit
    implementation "com.squareup.retrofit2:adapter-rxjava2:$RETROFIT_VERSION"
    implementation "com.squareup.retrofit2:converter-moshi:$RETROFIT_VERSION"
    implementation "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION"

    // dagger2
    implementation "com.google.dagger:dagger:$DAGGER_VERSION"
    implementation "com.google.dagger:dagger-android:$DAGGER_VERSION"
    implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
    kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
    kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
    implementation "com.squareup.okhttp3:logging-interceptor:4.2.2"

    // Android Architecture Components
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-rc03"
    implementation "androidx.lifecycle:lifecycle-livedata:2.2.0-rc03"
```
Gradleに`Dagger2`, `Retrofit2`, `AAC`を設定します。
~~Daggerは2.24、Retrofitは2.5.0が現状の最新です。~~
2019/12/26 バージョンが間違っていたので修正しました。
Daggerは2.25.3、Retrofitは2.7.0が最新となります。
**2020/01/07追記**
[JakeWharton](https://github.com/JakeWharton)氏曰く[「Gson is deprecated.」](https://www.reddit.com/r/androiddev/comments/ejjroe/kotlinx_json_vs_gson_a_short_article_about_kotlin/fcz3v0d/?utm_source=share&utm_medium=web2x)との事だったので、Gsonから[Moshi](https://github.com/square/moshi)に変更しました。

## 2.まずはApplicationとMainActivityの依存関係を注入
基本となるApplicationクラスを作成します。

```kotlin:App.kt
class App: Application()
```

AndroidManifest.xmlに登録するのを忘れずに。

```kotlin:AndroidManifest.xml
    <application
        android:name=".App"
```

AppをinjectするためにAppModuleを作成します。

```kotlin:AppModule.kt
@Module
abstract class AppModule {
    @Binds
    abstract fun provideContext(application: App): Context
}
```

---
※2019/12/27 10:40 修正
`@Component.Factory`を使用した方法に修正を行いました。

`@Component.Factory`を使用したDIについては[こちらの記事](https://medium.com/@star_zero/dagger%E3%81%AEcomponent-builder%E3%81%8B%E3%82%89component-factory%E3%81%B8%E3%81%AE%E7%A7%BB%E8%A1%8C-eb524575d5)を参考にさせて頂きました。

AppComponentを作成します。

```kotlin:AppComponent.kt
@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class]
)
interface AppComponent : AndroidInjector<App> {
    @Component.Factory
    interface Factory {
        fun create(@BindsInstance app: App): AppComponent
    }
}
```

`AndroidInjectionModule`を@Componentに追加します。
`AndroidSupportInjectionModule`の代わりに使えるModuleです。
`DispatchingAndroidInjector`の依存関係を解消してくれるそうです。

ここで、Appクラスに　`DaggerApplication` を継承させるように修正します。
必ず使わなければいけないものではありませんが、DaggerによるDIを少し楽にしてくれます。
`DaggerApplication` は `applicationInjector()` をオーバーライドする必要があります。
Appインスタンスをinject出来るように、以下のように実装します。

```kotlin:App.kt
class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.factory()
            .create(this)
    }
}
```

`DaggerAppComponent` が赤字になっていると思いますが、一旦ビルドすると、DaggerAppComponentが自動的に生成され、インポート出来るようになります。

ここまででAppクラスのDIが完了しました。

次に、Activityをinject出来るようにしていきます。

```kotlin:MainActivityBuilder.kt
@Module
abstract class MainActivityBuilder {
    @ActivityScope
    @ContributesAndroidInjector
    abstract fun bindMainActivity(): MainActivity
}
```
MainActivityのbindingに@ActivityScopeアノテーションをつけています。
同じScopeを指定したInjectは、ライフサイクルが共通（？）になります。

次に、MainActivityにDaggerAppCompatActivityを継承させます。
DaggerAppCompatActivityが`AndroidInjection.inject(this)`を代わりにやってくれているため、コードがすっきりします。

```kotlin:MainActivity
class MainActivity : DaggerAppCompatActivity()
```

AppComponentに、作成した `MainActivityBuilder` をmodulesとして追加します。

```kotlin:AppComponent.kt
@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        MainActivityBuilder::class] // 追加
)
interface AppComponent : AndroidInjector<App> {
    @Component.Factory
    interface Factory {
        fun create(@BindsInstance app: App): AppComponent
    }
}
```

ここまででビルドしてもらうと、普通にActivityが立ち上がるようになっているかと思います。

## 3. Fragmentの依存性を注入する
業務で実装するアプリはFragmentを使用する事が多いと思うので、Fragmentの依存性も注入します。
ここではMainFragmentという名前にします。

MainFragment用のModuleを追加します。

```kotlin:MainFragmentModule.kt
@Module
internal abstract class MainFragmentModule {
    @ContributesAndroidInjector
    abstract fun provideMainFragment(): MainFragment
}
```

これをMainActivityのModuleとして指定します。

```kotlin:MainActivityBuilder.kt
@Module
abstract class MainActivityBuilder {
    @ActivityScope
    @ContributesAndroidInjector(modules = [MainFragmentModule::class]) // 追加
    abstract fun bindMainActivity(): MainActivity
}
```

MainFragmentにはDaggerFragmentを継承します。
こうする事で`HasAndroidInjector`が実装されたFragmentとなるため、`AndroidSupportInjection.inject(this)`を記述しないで済みます。

```kotlin:MainFragment
class MainFragment : DaggerFragment() {
```

ここまででApp, Activity, Fragmentのinjectが完了しました。

長くなるので今回はここまで。
次回はRetrofitとViewModelのDIになります。

[【DI】Dagger2+Retrofit2(+OkHttp3)+ViewModelのDIの最小構成[その2]](https://qiita.com/m-coder/items/d8ddb5015f88fb7e588a)