Dagger Android拡張の使い方
DaggerのAndroid拡張について調べて試したりしたので、まとめておきます。Daggerをある程度理解できてる前提になっています。
ドキュメント
Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is developed by the…google.github.io
Android拡張とは
今までのやり方だと、コピペのコードが多かったり、Injectを行うクラスが依存解決する方法を知ってるとか、なんとか…っていう問題があるので、なんとかしようってことです。
ぼくの理解としては AndroidInjection.inject
でシンプルに依存性の注入をやっていこうっていう雑な解釈でいます。そのための準備がだいぶ複雑ですが。
なので、 AndroidInjection.inject
でちゃんと依存性の注入ができるようにするためにどう定義していくかということになります。
build.gradle
build.gradleの設定です。今回はKotlinを使っています。
シンプルな使い方
まずはActivityでシンプルに使う方法です。
AppModule
例として Repo
というものを依存解決させるものです。
引数の App
は Application
クラスを継承したやつです。あとで出てきます。
コメントにも書いてますが、引数の解決は @BindsInstance
のおかげで可能になっています。
ActivityModule
Activityで AndroidInjection.inject(this)
を使えるようにするために、 @ContributesAndroidInjector
というアノテーションを使っています。
abstractメソッドで戻り値にAndroidInjectionを使いたいActivityを書きます。
この方法とは別に @Subcomponent
を使った方法もありますが、こっちのほうがシンプルです。
AppComponent
先程つくったModuleを使ってComponentの定義を行います。
ここでは AndroidInjector
というinterfaceを継承して定義してます。
普通は inject
メソッドを定義すると思いますが、 AndroidInjector
がすでに定義してるので不要です。
@Component
に先程のModuleを指定してます。
中の @Component.Builder
では AndroidInjector.Builder
を継承して定義します。
App
Applicationクラスの定義です。
HasActivityInjector
interfaceを実装します。 activityInjector
メソッドを実装する必要があります。
Daggerが生成したComponentを使って DispatchingAndroidInjector
をInjectして、それを返却するようにします。
MainActivity
最後にActivityで実際にInjectしてみます。
AndroidInjection.inject
で依存性の注入を行います。
Fragmentで使う
次にFragmentで使う方法です。
MainActivityModule
Activityと同じように @ContributesAndroidInjector
を使って、 AndroidInjection.inject(this)
を使えるようにします。
(Support LibraryのFragmentは AndroidSupportInjection.inject
)
クラス名はMainActivityで使うので、MainActiivtyModuleとしています。
また、例として2つFragmentを使っています。
ActivityModule
こちらは前回と同じものですが、 modules
に先程のMainActivityModuleを追加しています。これによりSubComponentとして扱われるようになりActivityの依存解決を引き継げる感じになります。
MainActivity
Activity側も少し変更が必要です。
HasSupportFragmentInjector
を実装する必要があります。前回のApplicationクラスでやったことと同じ感じです。
Fragment
最後にFragment側のコードです。
AndroidSupportInjection.inject
で依存性の注入を行っています。Fragmentの場合は onAttach
で行います。
Module生成
これまで、Module生成はデフォルトコンストラクタのみでDaggerにまかせてましたが、それを自分で生成する方法です。
Android拡張とはちょっと外れるかもしれませんが、これまで使ってこなかった方法なので。
AppModule
AppModuleを少し変更しています。
コンストラクタに引数を追加したのと、メソッド引数も追加しました。
AppComponent
以下のようにAppComponent.Builderに2つメソッドを追加しています。
まず appModule
というabstractメソッドを追加して、戻り値をBuilder自身にしています。これによりComponent生成時にインスタンスを渡せるようになります。
また、 @BindsInstance
のメソッドも追加しています。これでAppModuleで使われてるメソッド引数を解決できるようになります。
App
あとはComponent生成時にAppComponentに定義したメソッドを使ってインスタンスを渡してあげます。
Activity/Fragmentごとのパラメーター
AppModuleようなグローバルなものではなく、Activity/Fragment単位で扱いたい依存解決のためのパラメーターがあると思います。
例えば、ActivityやFragmentのそのものインスタンスだったり、FragmentManagerだったり。
Dagger Android拡張の場合、依存注入を AndroidInjection.inject
でやるのでパラメーターを渡す方法がないので、Moduleの設定で解決してやる必要があります。
Model
サンプルとして単純なModelをコンストラクタインジェクションで作ってみます。引数にActivityとFragmentMangerを必要としています。
これをどのようにやっていくか見ていきます。
ActivityModule
ActivityModuleはFragmentを使うところでやったところと同じです。ここで指定してるMainActivityModuleのほうで色々やっていきます。
MainActivityModule
Module側の設定はこんな感じでちょっと複雑に見えます。
まず、 AndroidInjection.inject
を使うときに何かインスタンスを渡したりすることができなくなっています。そのためModuleのほうで頑張る感じです。
何もしない状態で使えるものとして @ContributesAndroidInjector
の戻り値に指定したMainActivityが依存解決できます。それを使って別のものに変換したり、取得したりする感じです。
@Binds
と @Provides
が使えれば、だいたい大丈夫かと思います。使い方はコメントの方を見てください。
それ以外は、前までに紹介したやり方と同様です。
Baseクラス
これまで、 HasActivityInjector
や HasSupportFragmentInjector
などを使ってきましたが、実はDaggerがBaseクラスのようなものを用意していて、これを使うと若干ですが記述が楽になります。
Supportライブラリ用とそうじゃない用があるので、package名を見て確認してください。
App
DaggerApplication
というのを継承して、AppComponentからAndroidInjectorを返すメソッドを用意するだけです。
MainActivity
Activityでは DaggerAppCompatActivity
(Supportライブラリの場合)を継承します。これだけInjectまでやってくれます。
AppComponent
1点注意として、AppComponent側に AndroidSupportInjectionModule
を追加してあげる必要があります。
これは DaggerApplication
が BroadcastReceiverやServiceの DispatchingAndroidInjector
を定義してるのでこれの依存を解決してあげる必要があるからです。
サンプルではApplicationとActivityしか紹介してませんが、FragmentやServiceのBaseクラスもあります。
サンプルコード
最後に、ぼくが確認用に雑に作ったプロジェクトを公開しておきます。