web-dev-qa-db-ja.com

Dagger 2注入コンストラクター

開発中のアプリケーションでDagger 2を使用し始めていますが、Dagger 2の動作方法についていくつか質問があります。

依存関係を初期化するための@Providesメソッドと@Injectアノテーションの背後にあるすべてのロジックを取得しますが、クラスコンストラクターへの@Injectアノテーションはバグのようなものです。

例えば:

私のアプリでは、アプリケーションのコンテキストを取得するために、ContextModuleというモジュールが1つ定義されています。

ContextModule.Java

@Module
public class ContextModule {

    private final Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context context() {
        return this.context;
    }
}

このモジュールは私のBaseActivityComponentによって使用されます:

BaseActivityComponent.Java

@BaseActivityScope
@Component(modules = ContextModule.class)
public interface BaseActivityComponent {
    void injectBaseActivity(BaseActivity baseActivity);
}

これまでのところとても良い..その後、コンテキストに依存するAuthControllerクラスがあり、それをBaseActivityに注入したいと思います。だから私のAuthControllers.classには次のようなものがあります:

public class AuthController {

    private Context context;

    @Inject
    public AuthController(Context context) {
        this.context = context;
    }

    public void auth() {
        // DO STUFF WITH CONTEXT
    }
}

そして、次のようにBaseActivityに注入します。

public class BaseActivity extends AppCompatActivity {

    @Inject
    AuthController authController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        BaseActivityComponent component = DaggerBaseActivityComponent.builder()
            .contextModule(new ContextModule(this))
            .build();

        component.injectBaseActivity(this);

        authController.auth();

    }
}

私の質問は、私のAuthControllersがBaseActivityの依存関係であることをdaggerがどのように認識するかです。宣言するだけで

@Inject
AuthController authController;

次のようなControllerModuleを作成した場合と同じです。

@Module(includes = ContextModule.class)
public class ControllerModule {

    @Provides
    AuthController authController(Context context) {
        return new AuthController(context);
    }

}

次に、BaseActivityComponentでAuthControllerゲッターを追加し、依存関係モジュールをControllersModuleに変更します。

@BaseActivityScope
@Component(modules = ControllersModule.class)
public interface BaseActivityComponent {

    void injectBaseActivity(BaseActivity baseActivity);

    AuthController getAuthController();
}

InjectBaseActivity(this)を呼び出すと、すべての@Injectアノテーションがクラスの依存関係であることを「短剣」に伝え、そのタイプに一致する@Injectアノテーション付きコンストラクターをプロジェクトで検索しますか?

Dagger 2の良い点は、モジュールファイルを依存関係3の "ドキュメント"として使用できることです。しかし、私が制御しているすべてのコンストラクターに@Injectを追加するだけでは、実際に何が何に依存しているかわからないので、少し混乱することはないでしょうか? (つまり、何が何に依存しているかを知っているので、実際に調べるには多くのファイルを参照する必要があります)

コンストラクターで@Injectアノテーションを使用するとき、またはモジュールファイルに@Providesメソッドを追加するときのベストプラクティスはありますか?コンストラクターで@Injectを使用すると、モジュールファイルのコンストラクター定義を変更する必要はありませんが、欠点はありますか?

ありがとう。

27
Thiago Loddi

InjectBaseActivity(this)を呼び出すと、すべての@Injectアノテーションがクラスの依存関係であることを短剣に「伝え」、次にそのタイプに一致する@Injectアノテーション付きコンストラクターをプロジェクトで検索しますか?

まさに。ただし、injectBaseActivityを呼び出したときには完了していませんが、すべてコンパイル時に発生します。これは注釈処理(実行時にリフレクションを使用する別の方法)の1つの方法です。

プロジェクトをビルドすると、build.gradleファイルに(依存関係として)含めるdagger-annotation-processorが、@Injectアノテーションで注釈付けされたすべてのフィールド、クラスなどのリストで呼び出され、依存関係をビルドします。それとグラフ。次に、グラフを解決し、グラフ上のアイテムのすべての依存関係を提供するソースコードを生成します。

injectBaseActivityは、前に生成されたコードを実行するだけで、すべての依存関係をオブジェクトに割り当てます。これは適切なソースコードであり、読み取りとデバッグが可能です。

これがコンパイル手順である理由は、簡単に言えば、パフォーマンスと検証です。 (例:依存関係のサイクルがある場合、コンパイルエラーが発生します)


daggerは、AuthControllersがBaseActivityの依存関係であることをどのように知るのですか?

@Inject
AuthController authController;

フィールドに注釈を付けることにより、@Inject daggerはAuthControllerが必要であることを認識します。ここまでは順調ですね。ここで、短剣はコントローラーを提供するいくつかの手段を探し、コンポーネント、コンポーネントの依存関係、およびコンポーネントモジュール内でコントローラーを探します。また、コンストラクターについてknowsであるため、クラスを独自に提供できるかどうかも調べます。

モジュールに含めない場合、daggerはオブジェクトコンストラクターをどのように認識しますか?

@Inject
public AuthController(Context context) { /**/ }

また、コンストラクタに注入で注釈を付けることで、AuthControllerと呼ばれるクラスがあり、インスタンス化するにはコンテキストが必要であることを短剣に伝えました。基本的にモジュールに追加するのと同じです。

モジュールに@Providesメソッドを使用するのは、コンストラクターに@Inject注釈を追加するだけのソースコードがない場合、またはオブジェクトをさらに初期化する必要がある場合です。またはあなたの場合...

[...]モジュールファイルは、依存関係ツリーの「ドキュメント」として使用できます[...]

はい、もちろんできます。しかし、プロジェクトが成長するにつれて、多くの不必要なコードを維持する必要があります。これは、コンストラクターの単純な注釈でも同じことができるからです。

コンストラクターで@Injectアノテーションを使用するとき、またはモジュールファイルに@Providesメソッドを追加するときのベストプラクティスはありますか?

異なるコンテキストに異なるバージョンを提供する場合(たとえば、2つの異なる方法でインターフェイスを実装する場合)、実装として提供するクラスを短剣に伝える@Bindsアノテーションもあります。

それ以外は、可能な場合は常にコンストラクター注入を使用する必要があると思います。何かが変更されても、コードの他の部分に触れる必要はありません。また、記述するコードが少ないため、バグを含めることができる場所が少なくなります。

また、Daggerは詳細を知ることで多くのことを最適化できます。不要なコードを実装する場合は、導入したオーバーヘッドを処理する必要があります。


もちろん、最終的にはあなたが最高だと思うものまですべてです。結局、youで動作する必要があるのはyourコード;)

35
David Medenjak