Skip to content
Go back

Android.Daggerメモ

· Updated:

v2.25のときに個人的に調べたものです。内容には間違いが含まれている場合があります。

概要

  • 依存性の注入Dependency Injection(DI)とは処理に必要なオブジェクトをロジックの外から渡してやるテクニックのこと
    • 処理側はインスタンスの生成方法を知らずに済むので、その間の結合度が低下する
  • DaggerはDIを実現するためのフレームワーク
    • インスタンスの生成と渡しを自動化する
    • 依存関係をコンパイル時に解決するため、実行時コストが小さい

使い方

  1. 依存関係のグラフを定義する
  2. グラフに対応する実装をビルド時に生成する
  3. 生成された実装を使ってDIを行う

依存関係の定義

受け取る側

  • あなたが定義するクラスに対して、依存性を注入したい箇所に@Injectを付ける
    • コンストラクタに付けると、コンストラクタ引数を介して間接的に注入される
    • メンバ変数に付けると、直接代入することで注入される
      • そのメンバ変数はDaggerから見えるようにする必要がある
注入の遅延
  • Provider<T>Lazy<T>getメソッドを呼び出したときにはじめてTを注入する
    • Lazyは最初の呼び出しで注入されたインスタンスをキャッシュする

提供する側

  • 依存関係を満たすためのメソッドを集めたクラスに@Moduleを付ける
    • メソッドには生成したい実装に対応した注釈を付ける
  • 慣例として、モジュール名は接尾辞Moduleを持つ
Provides
  • インスタンスを提供するメソッドに@Providesを付ける
    • そのメソッドは戻り値の型が要求されるたびに呼び出される
  • 引数があれば、メソッドが呼び出されるときに注入される
  • 慣例として、関数名はprovideという動詞を使う
Binds
  • @Bindsの付いた抽象メソッドはDaggerによって実装が生成される
    • その引数はただ1つのみ
    • その引数の型は戻り値の型に代入可能でなければならない
  • その実装は戻り値の型が要求されるたびに注入された引数をそのまま返す

グラフの構築

コンポーネント

  • 指定の形式で記述されたインターフェイスに@Componentを付ける
    • modules要素に列挙されたモジュールはDaggerが実装を生成するのに使われる
  • 慣例として、コンポーネント名は接尾辞Componentを持つ
  • Daggerはそのコンポーネントに対して接頭辞Daggerを持つ実装クラスを生成する
Provisionメソッド
  • DI処理によって生成されたインスタンスを取得するメソッド
    • そのメソッドは引数を持たない
    • 戻り値はTのほか、Provider<T>Lazy<T>にもできる
Member-injectionメソッド
  • インスタンスのメンバ変数に依存性を注入するためのメソッド
    • そのメソッドは以下の形式で宣言される
      • 引数を1つだけ持ち、何も返さない
      • 引数を1つだけ持ち、引数の値を返す
      • 引数を持たず、MemberInjector<T>を返す
        • この場合はMemberInjector.injectMembers(T)の呼び出しによって注入される
ファクトリ
  • 指定の形式で記述されたインターフェイスに@Component.Factory付ける
    • その定義はコンポーネントの内部に記述する
    • その本体はただ1つのメソッドを持つ
      • そのメソッドはコンポーネント型(の派生型)を戻り値とする
      • その引数は以下のインスタンスを渡すよう宣言される
        • 必須:非抽象クラスのモジュール
        • 必須:依存関係にあるコンポーネント
        • 任意:@BindsInstanceを付けた引数
          • その型に対して注入されるインスタンスをセットする
  • Daggerが生成する実装はこのファクトリのインスタンスを返す静的メソッドfactory()を定義する

サブコンポーネント

SUSPENDED

DI処理の組み込み

  • ビルド時に生成される接頭辞Daggerを持つコンポーネントの実装クラスを用いる
    • コンポーネントのファクトリを用いてコンポーネントを生成する
    • コンポーネントのメソッドを用いてインスタンスを生成したり、依存性を注入したりする

Android対応

  • DaggerはAndroidアプリでDIを行うための仕組みを備えている
  • @ContributesAndroidInjectorはそのモジュールのサブコンポーネントとしてのAndroidInjectorを提供するメソッドを生成する
  • DaggerApplicationDaggerActivityDaggerFragmentなどはthisへの注入を行うために継承される
    • その基底クラスはHasAndroidInjectorを実装し、onCreateonAttachで依存性の注入を行う

Applicationの用意

  • あなたのApplicationにDaggerApplicationを継承させる
    • Applicationのコンポーネントを保持し、applicationInjector()でそのインスタンスを返す
  • あなたのApplicationのコンポーネントにAndroidInjector<T>を継承させる
  • あなたのApplicationのコンポーネントにAndroidInjectionModuleをインストールする
  • あなたのApplicationのコンポーネントファクトリにAndroidInjector.Factory<T>を継承させる

Activityの追加

  • そのActivityにDaggerActivityを継承させる
  • そのActivityに対する@ContributesAndroidInjectorメソッドを宣言したモジュールをApplicationのコンポーネントにincludeする
  • そのActivityのコンポーネントにincludeしたいモジュールを@ContributesAndroidInjectormodules要素に列挙する

Fragmentの追加

  • あなたのFragmentにDaggerFragmentを継承させる
  • そのFragmentに対する@ContributesAndroidInjectorメソッドを宣言したモジュールをActivityのコンポーネントにincludeする
    • または、Activityに対する@ContributesAndroidInjectormodules要素に加える
  • そのFragmentのコンポーネントにincludeしたいモジュールを@ContributesAndroidInjectormodules要素に列挙する
ViewModelへ注入したい場合
  • ViewModelはActivityやFragmentのライフサイクルで管理される方が良い?
    • ViewModelProviderを介して生成する必要がある
  • 方法1:Provider<T>からgetされたViewModelのインスタンスを返すViewModelProvider.Factoryの実装を注入する

Scope

  • Daggerが管理するオブジェクトの寿命はコンポーネントに紐付いている
  • コンポーネントにスコープ注釈を付けると、その要素が同一スコープのみで構成されるように強制できる?
    • コンポーネント内に寿命の異なるオブジェクトが混入するとコンパイルエラーにできる?
  • スコープの付いたメソッドから提供されるインスタンスはコンポーネントによって保持される
    • インスタンスをキャッシュするDoubleCheck型を用いるようになる
  • スコープ注釈として@Singletonが定義されている

サンプルコード

class Foo

class Bar @Inject constructor(
    private val foo: Foo
)

class Baz {
    @Inject
    internal lateinit var bar: Bar

    init {
        DaggerFooBarBazComponent
            .factory()
            .create()
            .injectBaz(this)
    }
}

@Module
class FooBarBazModule {
    @Provides
    fun provideFoo(): Foo {
        return Foo()
    }
}

@Component(modules = [FooBarBazModule::class])
interface FooBarBazComponent {
    // Provisionメソッド
    fun getFoo(): Foo
    fun getBarProvider(): Provider<Bar>
    fun getLazyBaz(): Lazy<Baz>

    // Member-injectionメソッド
    fun injectBaz(baz: Baz)
    fun injectAndReturnBaz(baz: Baz): Baz
    fun getBazInjector(): MembersInjector<Baz>

    // コンポーネントのファクトリ
    @Component.Factory
    interface Factory {
        fun create(): FooBarBazComponent
    }
}

参考文献