web-dev-qa-db-ja.com

Dagger 2のスコープとサブコンポーネント

_Dagger2_を使用してアプリを改善し、コードを保守しやすくしようとしていますが、一般的なアイデアは捉えましたが、スコープが_Dagger2_によってどのように管理されているのかまだわかりません(プロジェクトに短剣を注入しました)。 ApplicationComonentコンポーネントを作成しましたが、私のプロジェクトでは完全に機能します。これが私のコードです。

_@Singleton
@Component(modules = {
        ApplicationModule.class,
        ThreadingModule.class,
        NetworkModule.class,
        DatabaseModule.class,
        ServiceModule.class,
        ParseModule.class,
        PreferencesSessionModule.class})

public interface ApplicationComponent {
    ActivityComponent activityComponent(ActivityModule activityModule);

    void inject(BaseActivity baseActivity);

    void inject(MainAppActivity mainAppActivity);

    void inject(MyApplication application);

    void inject(BaseFragment baseFragment);

    void inject(MyService service);

    void inject(RegistrationIntentService service);
}
_

次のようにMyApplicationクラスにコンポーネントインスタンスを作成します

_private void initializeAndInjectComponent() {
        mApplicationComponent =
                DaggerApplicationComponent
                        .builder()
                        .threadingModule(new ThreadingModule(1))
                        .applicationModule(new ApplicationModule(this))
                        .networkModule(new NetworkModule(
                                MyService.API_SERVER_BASE_URL,
                                MyService.TIMEOUT))
                        .build();
        mApplicationComponent.inject(this);
    }
_

そして、Activitiesに注入するためのコンポーネントを取得できます

_    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().inject(this);
_

すべてが完璧に動作します。

各メソッドとモジュールクラスを追加するには、_@Singleton_スコープで注釈を付けます。ApplicationComponentに関連するすべてのモジュール

依存関係を改善したいので、_@PerActivity_、_@PerFragment_などのカスタムスコープを持つ多くの例を見てきました。多くの質問がありますが、これについては後で説明します。

だから私はActivityComponentを作成しました

_@PerActivity
@Subcomponent(
        modules = {
                NetworkServiceModule.class,
                ActivityModule.class,
                PermissionModule.class
        })
public interface ActivityComponent {
    Activity activity();

    void inject(BaseActivity baseActivity);
}
_

すべてのモジュールはこのようになります

_@PerActivity
@Module
public class ActivityModule {
    private Activity mActivity;

    public ActivityModule(Activity activity) {
        this.mActivity = activity;
    }

    @Provides
    @PerActivity
    Activity provideActivity() {
        return this.mActivity;
    }
}
_

BaseActivityに次の依存関係があります

_// Dependencies from ApplicationComponent
    @Inject
    protected ApplicationSettingsManager mApplicationSettingsManager;
    @Inject
    protected ScheduledThreadPoolExecutor mPoolExecutor;
// Dependencies from ActivityComponent
    @Inject
    protected SpiceManager mSpiceManager;
    @Inject
    protected PermissionController mPermissionController;
_

そして、私のonCreate()メソッドでは、次のように注入しています

_    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().activityComponent(new ActivityModule(this)).inject(this);
_

サブコンポーネントActivityComponentを作成する前は

_   MyApplication application = MyApplication.get(this);
        application.getApplicationComponent().inject(this);
_

エラーが発生しました

_Error:(34, 10) error: com.octo.Android.robospice.SpiceManager cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
BaseActivity.mSpiceManager
[injected field of type: com.octo.Android.robospice.SpiceManager mSpiceManager]
_

どこに問題があるのか​​、見逃してしまったのかわかりません。 dagger2のスコープに関する私の質問。

_@Singleton_以外はDagger 2で無視されますか?コンポーネントの寿命がどのように管理されているのかわかりません。私はたった一つのアイデアを持っています

  1. _@Singleton_アノテーションを使用すると、短剣はアプリケーションのライフサイクル全体で存在する静的プールにオブジェクトを作成し、JVMプロセス(dalvik VM、ART)インスタンスが破棄されると破棄されます。

  2. 他の注釈を使用する場合は、開発者がコードを適切に維持するためだけに必要です。_@PerActivity_、_@PerFragment_は、単なるカスタム注釈です。また、_@PerFragment_コンポーネントをアプリケーションクラスに配置した場合、アプリケーションが存続する限り存続します。私は正しいですか?

  3. だから私はこれをこのように理解し、daggerが_@Singleton_アノテーションを見つけると、コンポーネントが最初に作成されたときにコンポーネントへの静的参照を追加し、他のアノテーションの場合はコンポーネントへの参照を保持しません。

上記の問題の助けに感謝します。

[〜#〜]更新[〜#〜]

_David Medenjak_の回答ありがとうございます。_Dagger2_についてよく理解できました。

私は問題を見つけたばかりですが、今は別のActivityコンポーネントを使用している限り、ApplicationComponentの2行を忘れて、MainActivityのinejctionをActivityComponentではなくApplicationComponentに変更して、サブコンポーネントからの依存関係を解決できないことを確認しました。

_ void inject(BaseActivity baseActivity);

 void inject(MainAppActivity mainAppActivity);
_

これですべてが完璧に動作し、_Dagger2_と分離アーキテクチャが好きです。

32
CROSP

少し過激ですが、物事を単純化するために:すべてのScopeアノテーションは、@Singletonを含め、構文上の砂糖にすぎません。

スコープは主にコンパイル時のチェックを提供するだけです。循環依存関係、または見逃した可能性のあるものに関するエラー。 @Singletonは他のスコープと同じですが、唯一の違いは、既存のアノテーションであり、自分で作成する必要がないことです。代わりに@MySingletonを使用できます。

[...] daggerは、アプリケーションのライフサイクル全体で存在する静的プールにオブジェクトを作成しています

いいえ、ダガーは何もしません静的です。コンポーネントオブジェクトがあります。これらのコンポーネントは、モジュールによって作成されたオブジェクトを保持します。コンポーネント内のオブジェクトにコンポーネントのスコープがある場合、そのオブジェクトはその正確なコンポーネント内に1回だけ作成されます。 2つのAppComponentオブジェクトを作成する場合、@Singleton注釈付きオブジェクトごとに2つのオブジェクトがあり、それぞれがコンポーネント内にあります。これがあなたがすべき理由ですコンポーネントへの参照を保持します。私が見たり使用したりしたほとんどの実装は、それらのAppComponentApplication内に保持します。これを行うと、シングルトンのようにlikeを使用できます。これはまだ単なるPOJOです。

[...] @PerFragmentコンポーネントをApplicationクラスに配置すると、Applicationが存続する限り存続します。

はい。上記の段落ですでに説明したように、これは単なるオブジェクトです。参照を維持し、オブジェクトを維持します。それを捨てるか、新しいオブジェクトを作成し、新しいオブジェクト(このコンポーネント/スコープ内で定義されている)を取得します。ただし、しないでくださいアクティビティまたはフラグメントスコープコンポーネントは、アクティビティまたはフラグメント以外の場所にそれぞれ保持する必要があります。アプリコンポーネントでメモリリークが発生する可能性があります。 (そうでない場合は、おそらくアクティビティまたはフラグメントのスコープは必要なかったでしょう。)

daggerが@Singletonアノテーションを見つけると、コンポーネントが最初に作成されたときにコンポーネントへの静的参照を追加し、他のアノテーションの場合はコンポーネントへの参照を保持しません。

繰り返しますが、違います。静的なものはありません。単純な古いJavaオブジェクト。独自のオブジェクトを使用して複数の@Singletonコンポーネントを持つことができますが、おそらくそうすべきではありません(これにより、インスツルメンテーションテストが可能/簡単になりますが、コンポーネントを交換するだけです) 。)


あなたの言及されたエラー

SpiceManagerは、@ Injectコンストラクターなしで、または@ Provides-または@ Produces-annotatedメソッドから提供できません。

つまり、オブジェクトを挿入しようとしているコンポーネントは、SpiceManagerを生成または提供する方法を見つけることができません。 AppComponentまたは他の場所から提供していること、アノテーションなどがないことを確認してください。

60
David Medenjak