2017-06-19 Dagger 2 の第一印象
昨日 (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
そんな感じ。たぶん、また書く。