Dagger2 入門解説

  • 39
    Like
  • 0
    Comment

本稿ではDependencyInjectionとは何か、なぜDIする必要があるのかといった事は解説致しません。
執筆にあたり参照しているDagger2のバージョンは2.5です。
間違いがありましたら、お手数ですが遠慮なくご指摘をお願い致します。
サンプルコードにセミコロンがまったくないですが、そっとしておいてください。

Module/Component

DaggerでDependencyInjectionを行うにはModuleとComponentを実装する必要があります。

Module

InjectしたいDependencyを提供する役目を負っています。

Component

ModuleとDependencyをInjectされる側をつなぐ役目を負っています。
(つなぐ役目とういう説明は正確ではありませんが、入門時点ではそのように理解したほうがキャッチアップし易いでしょう。)

SubComponent

ComponentにたいしてさらにComponentを追加することが可能です。
SubComponentを利用することでComponentを階層構造化することができます。
階層構造下することのメリットは後述します。

Component構成

  1. Application
  2. Activity
  3. (Fragment)

採用しているアーキテクチャによりますが、Androidアプリケーション開発では上記のAndroidアプリケーション構成要素にそってComponentを構成するパターンが多いように感じます。

  1. ApplicationComponent
  2. ActivityComponent(ApplicationComponentのSubComponent)
  3. (FragmentComponent(ActivityComponentのSubComponent))

ApplicationComponent

Applicationクラスを継承したクラスで利用し、Applicationクラスのライフサイクルにそって生成するComponentです。
OkHttpClientやRetrofitとRetrofit#createで生成するService、OrmaDatabaseなどアプリケーション内でSingletonに扱うべきものをもたせることが多いです。

ActivityComponent

Activityを継承したクラスで利用し、Activityのライフサイクルにそって生成するComponentです。
ApplicationComponentが提供するオブジェクトを利用して生成するオブジェクトや、Activityのライフサイクル内でしか扱えないものをもたせることが多いです。
たとえば、Retrofit#createで生成したServiceをコンストラクタで引き受けるApiClientやOrmaDatabaseをコンストラクタで引き受けるDatabaseAccessObjectなどが該当します。

ApplicationComponentのSubComponentとして構成することで、ApiClientをActivity毎に生成しなおしたり、Activity間でSingletonにしたりといった事ができるようになります。

参考:Dagger 2でAndroidのライフサイクルごとのSingletonを実現する

実装

Module

@Module
public class ApplicationModule {
    private final Context context

    public ApplicationModule(Application app) {
        context = app
    }

    @Singleton
    @Provides
    public OkHttpClient provideOkHttpClient() {
        return OkHttpClient.Builder()
                ...
                .build()
    }

    @Singleton
    @Provides
    public Retrofit provideRetrofit(OkHttpClient okHttpClient) {
        return Retrofit.Builder()
                ...
                .build()
    }

    @Singleton
    @Provides
    public GithubService provideGithubService(Retrofit retrofit) {
        return retrofit.create(...)
    }

    @Singleton
    @Provides
    public OrmaDatabase provideOrmaDatabase() {
        return OrmaDatabase.builder(context)
                ...
                .build()
    }
}
  1. Moduleクラスには@Moduleアノテーションをつけます。
  2. Injectしたいオブジェクトを提供するメソッド(プロバイドメソッド)には@Providesアノテーションをつけます。
  3. プロバイドメソッドは引数を取ることが出来ませんが、プロバイドされている型であれば引数として取ることが出来ます。(もちろん、上位のComponentが持つ型に関しても解決できます)
  4. @Singletonをつけるとその名の通り、プロバイドするオブジェクトをSingletonとして扱うことが出来ます。

Component

@Component({ ApplicationModule.class })
interface ApplicationComponent {
    void inject(FooApplication application)
}
  1. Componentには@Componentをつけます。
  2. Componentアノテーションの引数にはそのComponentに対応するModuleのClassクラスを渡しましょう。
  3. injectメソッドの引数には依存性を注入したクラスを指定します。

Componentにinjectメソッドを定義すると、後述するFooApplicationの@InjectアノテーションがつけられたフィールドにたいしてApplicationModuleから取得したオブジェクトを代入するコードが自動生成されます。

Inject

public class FooApplication extends Application {

    @Inject
    OrmaDatabase orma;

    public void onCreate() {
        ApplicationComponent component = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build()

        component.inject(this)
    }

}
  1. DaggerXxx(Component名).builder()でComponentのビルダーを取得します。
  2. builderには対応するmodule名のメソッドが実装されているので、Moduleを渡します。
  3. buildメソッドでComponentを取得します。
  4. このcomponentは先程定義したインターフェースを実装しています。injectメソッドをつかって依存性を注入してください。

SubComponent

@SubComponent({ ActivityModule.class })
interface ActivityComponent {
    void inject(FooActivity activity)
}

(ActivityModuleに関しては省略)

  1. SubComponentには@SubComponentをつけます
  2. その他はComponentと同様です。
@Component({ ApplicationModule.class })
interface ApplicationComponent {
    void inject(FooApplication application)

    ActivityComponent plus(ActivityModule module)
}
  1. Componentに対してSubComponentと対応したModuleを引数に取り、SubComponentを返すメソッドを定義します。
class FooActivity {

    @Inejct
    FooDao dao

    public void onCreate(...) {
        ApplicationComponent applicationComponent = ((FooApplication)getApplication()).getComponent()
        ActivityComponent activityComponent = applicationComponent.plus(new ActivityModule())
        activityComponent.inject(this)
    }
}
  1. ApplicationにApplicationComponentへのアクセサを実装しておきます
  2. ApplicationComponent#plusに対してActivityModuleを渡してActivityComponentを取得します
  3. ActivityComponent#injectを利用してInjectします。

Constructor Injection

public class Dao {
    @Inject
    public Dao(OrmaDatabase orma) {

    }
} 

プロバイドメソッドを実装せずとも、Constructor Injectionを利用することで依存性を注入することが可能です。
すでにプロバイドメソッドが実装されているオブジェクトにのみ依存しているコンストラクタの場合、コンストラクタに@Injectをつけると、プロバイドメソッドを実装せずともこのように利用することが可能です。

public class FooActivity {
    @Inject
    Dao dao
}