LoginSignup
10
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-25

はじめに

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

実装

1. Gradle設定

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氏曰く「Gson is deprecated.」との事だったので、GsonからMoshiに変更しました。

2.まずはApplicationとMainActivityの依存関係を注入

基本となるApplicationクラスを作成します。

App.kt
class App: Application()

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

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

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

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

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

@Component.Factoryを使用したDIについてはこちらの記事を参考にさせて頂きました。

AppComponentを作成します。

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を少し楽にしてくれます。
DaggerApplicationapplicationInjector() をオーバーライドする必要があります。
Appインスタンスをinject出来るように、以下のように実装します。

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

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

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

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

MainActivityBuilder.kt
@Module
abstract class MainActivityBuilder {
    @ActivityScope
    @ContributesAndroidInjector
    abstract fun bindMainActivity(): MainActivity
}

MainActivityのbindingに@ActivityScopeアノテーションをつけています。
同じScopeを指定したInjectは、ライフサイクルが共通(?)になります。

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

MainActivity
class MainActivity : DaggerAppCompatActivity()

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

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を追加します。

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

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

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

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

MainFragment
class MainFragment : DaggerFragment() {

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

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

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

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4