Member preview

Dagger Android拡張の使い方

DaggerのAndroid拡張について調べて試したりしたので、まとめておきます。Daggerをある程度理解できてる前提になっています。

ドキュメント

Android拡張とは

今までのやり方だと、コピペのコードが多かったり、Injectを行うクラスが依存解決する方法を知ってるとか、なんとか…っていう問題があるので、なんとかしようってことです。

ぼくの理解としては AndroidInjection.inject でシンプルに依存性の注入をやっていこうっていう雑な解釈でいます。そのための準備がだいぶ複雑ですが。

なので、 AndroidInjection.inject でちゃんと依存性の注入ができるようにするためにどう定義していくかということになります。


build.gradle

build.gradleの設定です。今回はKotlinを使っています。


シンプルな使い方

まずはActivityでシンプルに使う方法です。

AppModule

例として Repo というものを依存解決させるものです。

引数の AppApplication クラスを継承したやつです。あとで出てきます。

コメントにも書いてますが、引数の解決は @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クラス

これまで、 HasActivityInjectorHasSupportFragmentInjector などを使ってきましたが、実は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クラスもあります。


サンプルコード

最後に、ぼくが確認用に雑に作ったプロジェクトを公開しておきます。