昨日 (2017-06-18) のことだけど、ようやく Dagger 2 を bouzuya/bbna に適用した。第一印象を書いておく。
導入は簡単だった。
依存関係は app/build.gradle
の dependencies
に次の二行を入れて Android Studio の Sync Now で良い。
compile 'com.google.dagger:dagger:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
最初につくる要素をいくつか挙げる。
@Module
アノテーションをつけたXXXModule
クラス@Provides
アノテーションをつけたprovideYYY
メソッドを持つ
@Component
アノテーションをつけたXXXComponent
インタフェース@Inject
アノテーションをつけたコンストラクタやフィールドを持つXXX
ここまでつくると DaggerXXXComponent
クラスが各アノテーションから生成される。DI コンテナってリフレクションで処理しているイメージを持っていたのだけど、 Dagger 2 はアノテーションプロセッサでクラスを生成するらしい。この DaggerXXXComponent
の中身を見て、挙動が分かる雰囲気に安心した。
DaggerXXXComponent
は static Builder builder()
を持っているので、 XXXComponent component = DaggerXXXComponent.builder().build();
な形で生成する。
ここまでを雰囲気で書くと↓な感じ。
interface YYY { void foo(); } class YYYImpl { void foo() { /* ... */ } } @Module class XXXModule { @Provides YYY providesYYY() { return new YYYImpl(); } } @Component(modules = {XXXModule.class}) interface XXXComponent { void inject(XXX xxx); } class XXX extends Application { @Inject YYY yyy; @Override void onCreate() { XXXComponent component = DaggerXXXComponent.builder().build(); component.inject(this); } }
これだけでは inject()
とは言うものの Dependency Injection より Service Locator のほうが適当っぽい。 ButterKnife の ButterKnife.bind(this)
が雰囲気は近い。あと Android の制約として Activity / Fragment の生成がシステムの制御下なので、 Activity への生成を DI ライブラリではできない。 Activity / Fragment へは何かしらの形で明示的な inject が要る。簡素化する仕組みは公式でも提供されているらしい。
DaggerXXXComponent
だけど、中身は Provider<T>
と Injector<T>
でつぎはぎしている感じ。これらはインタフェースだけど、 @Provide
と @Inject
に対応する形でクラスが自動生成される。 Provider
の実装は生成タイミングの調整や連鎖的な依存関係を解決するためのファクトリっぽい。 Injector
は各パッケージに配置する (フィールドインジェクションだと同一パッケージでないとね) ためのクラスっぽい。
まだ @Scope
/ @Subcomponent
/ @Qualifier
などには触れていない。どう @Component
を割るべきかなども考えられていない。また調べる。
公式がいまひとつピンとこないので、↓をはじめとした外部サイトをよく見ている。
https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2
そんな感じ。たぶん、また書く。