web-dev-qa-db-ja.com

シングルをオーバーライドするDagger2の問題は、アプリが使用するライブラリ内のモジュールから注釈付きメソッドを提供します

GitHubプロジェクトリンク

GitHub上のプロジェクト を作成しました。これは、私のプロジェクトの実際のアーキテクチャの短剣2アーキテクチャのモデルです。この質問は、GitHubプロジェクトに基づいています。

この質問では多くのコードスニペットを提供しましたが、問題を理解するには、Android Studioでプロジェクトを自分でコンパイルする方が簡単な場合があります。

コードをチェックアウトすると、コンパイルされません。 AppModule.Javaに移動し、両方の提供メソッドをコメントアウトすると、コンパイルされます。

主な質問は、この投稿の最後の行です。

https://github.com/qazimusab/Dagger2LibraryProject

アーキテクチャ

アプリケーションの作成に必要なすべてのコードを含むライブラリがあります。このアーキテクチャのポイントは、プロジェクトで作成する各アプリがライブラリを使用でき、短剣2を介して、独自のモジュールで必要な単一のクラスまたはアクティビティに異なる実装を提供できる必要があることです。この時点で、このサンプルプロジェクトには、ライブラリを使用するアプリケーションが1つしかありません。

問題

ダガー1では、同じアーキテクチャを使用しており、(ライブラリモジュールではなく)アプリ固有のモジュールで、ライブラリモジュールのいずれかで提供されていた実装をオーバーライドするための新しい提供注釈付きメソッドを追加できました。限り

  1. メソッドはアプリモジュールのモジュールにありました
  2. メソッドには@Providesアノテーションが付けられました
  3. メソッドの戻り値の型は、オーバーライドするものと同じでした。

Dagger 2では、提供をオーバーライドしない場合、またはオーバーライドする場合、そのモジュール内のすべての提供をオーバーライドし、そのモジュールをアプリケーション固有のモジュールのインクルードから削除すると、アーキテクチャが機能します。

たとえば、私のプロジェクトには、アプリとライブラリがあります。

アプリにはAppModuleがあります。ライブラリには、CatとCatFoodを提供するCatModule、DogとDogFoodを提供するdogモジュール、およびアクティビティを提供するLibraryModuleがあります。

CatModule.Java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Cat; import
com.example.qaziahmed.library.classes.CatFood; import
com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.ICatFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
CatModule {

    @Provides
    @Singleton
    ICat provideCat() {
        return new Cat();
    }

    @Provides
    ICatFood provideCatFood(){
        return new CatFood();
    } }

DogModule.Java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Dog; import
com.example.qaziahmed.library.classes.DogFood; import
com.example.qaziahmed.library.classes.contract.IDog; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
DogModule {

    @Provides
    @Singleton
    IDog provideDog() {
        return new Dog();
    }

    @Provides
    IDogFood provideDogFood(){
        return new DogFood();
    }

}

したがって、私のアプリケーションモジュールでは、汎用猫の代わりにICatのハウス猫実装を提供し、通常のドッグフードの代わりにIDogFoodのAllNaturalDogFood実装を提供したいと思います。次に、私のAppModuleに、それらをオーバーライドする2つの提供を追加します。

AppModule.Java

package com.example.qaziahmed.dagger2libraryproject.application;

import
com.example.qaziahmed.dagger2libraryproject.classes.AllNaturalDogFood;
import com.example.qaziahmed.dagger2libraryproject.classes.HouseCat;
import com.example.qaziahmed.library.application.modules.CatModule;
import com.example.qaziahmed.library.application.modules.DogModule;
import
com.example.qaziahmed.library.application.modules.LibraryModule;
import com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by ogre on 2015-07-12  */ @Module(includes = {
        LibraryModule.class,
        DogModule.class,
        CatModule.class }) public class AppModule {

    @Provides
    @Singleton
    ICat provideHouseCat() {
        return new HouseCat();
    }

    @Provides
    IDogFood provideAllNaturalDogFood(){
        return new AllNaturalDogFood();
    } }

さて、このセットアップを実行すると、これは私が得るエラーです:

エラー:com.example.qaziahmed.library.classes.contract.ICatが複数回バインドされています:@Provides @Singleton com.example.qaziahmed.library.classes.contract.ICat com.example.qaziahmed.dagger2libraryproject.application.AppModule.provideHouseCat ()@Provides @Singleton com.example.qaziahmed.library.classes.contract.ICat com.example.qaziahmed.library.application.modules.CatModule.provideCat()エラー:com.example.qaziahmed.library.classes.contract。 IDogFoodは複数回バインドされます:@Provides com.example.qaziahmed.library.classes.contract.IDogFood com.example.qaziahmed.dagger2libraryproject.application.AppModule.provideAllNaturalDogFood()@Providescom.example.qaziahmed.library.classes.contract。 IDogFood com.example.qaziahmed.library.application.modules.DogModule.provideDogFood()

ここで、AppModule.Javaの場合、CatFoodとProvideDogを提供する注釈付きメソッドも追加し、AppModuleのインクルードからCatModule.classとDogModule.classを削除すると機能します。

ただし、全体的な問題は、ライブラリ内の一部のモジュールで単一のprovidesメソッドをオーバーライドし、その特定のモジュール内のすべてのprovides注釈付きメソッドをオーバーライドしてからそのモジュールをインクルードから削除する方法です。 AppModule.Java

15
qazimusab

Dagger2ドキュメントからこの引用を復号化しようとします。

Dagger2はオーバーライドをサポートしていません。単純なテスト偽物をオーバーライドするモジュールは、モジュールのサブクラスを作成して、その動作をエミュレートできます。オーバーライドを使用し、依存性注入に依存するモジュールは、オーバーライドされたモジュールが2つのモジュール間の選択として表されるように分解する必要があります。

現在の例では、provides*メソッドが単純な新しいオブジェクトを作成するため、依存性注入に依存していません。そのため、モジュールのサブクラスを作成し、オーバーライドする必要のあるprovidesメソッドをオーバーライドして、それを含めることができます。コンポーネントの新しいモジュール。

DIに依存している場合(実際には、プロジェクトのある段階で)、次のようになります。

@Provides
@Singleton
ICat provideCat(IBowtie bowtie) { // 'bowtie' needs to be injected
    return new CatWithBowtie(Bowtie);
}

「オーバーライドを使用し、依存性注入に依存するモジュールは分解する必要があります」ということになると、基本的には、CatModuleを2つに分割する必要があります:CatModuleprovidesCat、および 'CatFoodModule'とprovideCatFood()。次に、アプリのコンポーネントで、CatWithBowtieModuleの代わりに新しいCatModuleを使用します。

2つの役立つアドバイスがあります。

  1. ライブラリプロジェクトではモジュールを分割するため、モジュールごとにprovides*メソッドは1つだけです。はい、BSのように聞こえますが、これがアプリの後半で簡単にオーバーライドできる唯一の方法です。

  2. しばらくの間、ライブラリがサードパーティからJAR/AAPとして提供されており、ソースさえ持っていない場合を考えてみましょう。その場合、libで定義されたモジュールを再利用することはできないため、すべてを自分で作成する必要があります。これはまさにDagger2で起こることです。

アプリでlibのモジュールを直接使用しようとすると(行ったように)、これら2つのプロジェクトは2つの別個のプロジェクトではなく、2つのプロジェクト(clusterf * ckが緊密に結合されている)のように見える1つのプロジェクトになります。アプリがlibに依存することは問題ありませんが、libがアプリに依存することはnot OKです。つまり、Dagger 2では、クロス(プロジェクト)境界modulesおよびcomponentsを使用しないのが最善です

「アプリでlibのmodules/componentsを使用できない場合、libでDagger 2を使用することの利点は何ですか?」と尋ねる人がいるかもしれません。さて、あなたはまだあなたのユニットテストであなたの短剣modules/componentsを使うことができます。それは結局のところ短剣を使うことの主な利点です。また、libが他の人によって使用されることを意図している場合は、物事を「配線」する方法を示す参照アプリを提供して、libのユーザーが自分に合っている場合はそのコードをコピーするか、少なくともその方法を確認できるようにすることができます。始めること。

9
Ognyan

問題は、インジェクションが同じオブジェクトを提供している2つのメソッドを認識していることです。

このリンクを読んだ場合: http://google.github.io/dagger/ プロバイダーに次のように名前を付けることでこれを修正できます。

@Provides @Named("water")

次に、インジェクションで、次のように参照します。

@Inject @Named("water")
8
JFPicard

回避策として、@Provides用と@Overrides用の2つのメソッドを作成します。

@Override
protected X getX() {
    return new X();
}

@Provides
X provideX() {
    return getX();
}
0
theapache64