Dagger 2のコンポーネント(オブジェクトグラフ)のライフサイクルを決定するものは何ですか?


134

私はDagger 2のスコープ、特にスコープ付きグラフのライフサイクルに頭を抱えようとしています。スコープを離れたときにクリーンアップされるコンポーネントをどのように作成しますか。

Androidアプリケーションの場合、Dagger 1.xを使用すると、通常、アクティビティレベルで子スコープを作成するために拡張するアプリケーションレベルにルートスコープがあります。

public class MyActivity {

    private ObjectGraph mGraph;

    public void onCreate() {
        mGraph = ((MyApp) getApplicationContext())
            .getObjectGraph()
            .plus(new ActivityModule())
            .inject(this);
    }

    public void onDestroy() {
        mGraph = null;
    }
}

子スコープへの参照を保持している限り、子スコープは存在しました。この場合は、アクティビティのライフサイクルでした。onDestroyで参照を削除することで、スコープ付きグラフがガベージコレクションされるのを自由にできるようになりました。

編集

ジェシー・ウィルソンは最近メジャーカルパを投稿しました

Dagger 1.0はスコープ名をひどく台無しにしています... @Singletonアノテーションはルートグラフとカスタムグラフの両方に使用されているため、実際のスコープが何かを理解するのは難しいです。

そして、私が読んだり聞いたりしたすべてのことは、スコープの動作を改善するDagger 2に向けられていますが、その違いを理解するのに苦労しています。以下の@Kirill Boyarshinovのコメントによると、コンポーネントまたは依存関係のライフサイクルは、通常どおり、具体的な参照によって決定されます。では、Dagger 1.xスコープと2.0スコープの違いは、純粋に意味の明確さの問題ですか?

私の理解

ダガー1.x

依存関係はどちら@Singletonかでした。これは、ルートグラフとサブグラフの依存関係にも同様に当てはまり、依存関係がどのグラフにバインドされているかが曖昧になります(「ダガー内」は、キャッシュされたサブグラフ内のシングルトンであるか、新しいアクティビティサブグラフのときに常に再作成されますか?構築されていますか?

ダガー2.0

カスタムスコープを使用すると、意味的に明確なスコープを作成できますが、機能的に@SingletonはDagger 1.xでの適用と同等です。

// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
    void inject(Application app);
}

@Module
public class MyAppModule {

    @Singleton @Named("SingletonScope") @Provides
    StringBuilder provideStringBuilderSingletonScope() {
        return new StringBuilder("App");
    }
}

// Our custom scope
@Scope public @interface PerActivity {}

// Activity level
@PerActivty
@Component(
    dependencies = MyAppComponent.class,
    modules = MyActivityModule.class
)
public interface MyActivityComponent {
    void inject(Activity activity);
}

@Module
public class MyActivityModule {

    @PerActivity @Named("ActivityScope") @Provides
    StringBuilder provideStringBuilderActivityScope() {
        return new StringBuilder("Activity");
    }

    @Name("Unscoped") @Provides
    StringBuilder provideStringBuilderUnscoped() {
        return new StringBuilder("Unscoped");
    }
}

// Finally, a sample Activity which gets injected
public class MyActivity {

    private MyActivityComponent component;

    @Inject @Named("AppScope")
    StringBuilder appScope

    @Inject @Named("ActivityScope")
    StringBuilder activityScope1

    @Inject @Named("ActivityScope")
    StringBuilder activityScope2

    @Inject @Named("Unscoped")
    StringBuilder unscoped1

    @Inject @Named("Unscoped")
    StringBuilder unscoped2

    public void onCreate() {
        component = Dagger_MyActivityComponent.builder()
            .myApplicationComponent(App.getComponent())
            .build()
            .inject(this);

        appScope.append(" > Activity")
        appScope.build() // output matches "App (> Activity)+" 

        activityScope1.append("123")
        activityScope1.build() // output: "Activity123"

        activityScope2.append("456")
        activityScope1.build() // output: "Activity123456"

        unscoped1.append("123")
        unscoped1.build() // output: "Unscoped123"

        unscoped2.append("456")
        unscoped2.build() // output: "Unscoped456"

    }

    public void onDestroy() {
        component = null;
    }

}

使用@PerActivityすると、このコンポーネントのライフサイクルに関する意図が伝達されますが、最終的にはコンポーネントをいつでもどこでも使用できます。Daggerの唯一の約束は、特定のコンポーネントについて、スコープの注釈が付けられたメソッドが単一のインスタンスを返すことです。また、Dagger 2はコンポーネントのスコープアノテーションを使用して、モジュールが同じスコープ内またはスコープ外の依存関係のみを提供することを確認することも想定しています。

要約すれば

依存関係は依然としてシングルトンまたは非シングルトンのいずれか@Singletonですが、現在はアプリケーションレベルのシングルトンインスタンスを対象としており、カスタムスコープはシングルトンの依存関係に短いライフサイクルでアノテーションを付けるための推奨される方法です。

開発者は、不要になった参照を削除することでコンポーネント/依存関係のライフサイクルを管理し、コンポーネントが意図されたスコープ内で1回だけ作成されるようにしますが、カスタムスコープアノテーションにより、スコープを簡単に識別できます。 。

64,000ドルの質問*

Dagger 2のスコープとライフサイクルに関する私の理解は正しいですか?

*実際には$ 64'000の質問ではありません。


5
あなたは何も逃しませんでした。各コンポーネントのライブサイクルの管理は手動です。私自身の経験から、ダガー1も同じでした。plus()新しいグラフへの参照を使用してアプリケーションレベルのObjectGraphオブジェクトをサブグラフ化すると、アクティビティに格納され、そのライフサイクルにバインドされました(で逆参照onDestroy)。スコープに関しては、コンポーネントの実装がコンパイル時にエラーなしで生成され、すべての依存関係が満たされていることを保証します。したがって、それは文書化の目的だけではありません。このスレッドからいくつかの例を確認してください。
キリルボヤルシノフ2015

1
これを明確にするために、「スコープ外」のプロバイダーメソッドは、すべての注入で新しいインスタンスを返しますか?
user1923613

2
なぜあなたはcomponent = nullを設定するのですか?onDestroy()?
マリアンPaździoch16年

回答:


70

ご質問は

Dagger 2のコンポーネント(オブジェクトグラフ)のライフサイクルを決定するものは何ですか?

簡単に言えば、あなたがそれを決定することです。コンポーネントには、次のようなスコープを与えることができます

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

これらは、次の2つの点で役立ちます。

  • スコープの検証:コンポーネントは、スコープのないプロバイダー、またはコンポーネントと同じスコープのスコープのプロバイダーのみを持つことができます。

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Module
public class ApplicationModule {
    @ApplicationScope //application-scoped provider, only one can exist per component
    @Provides
    public Something something() {
         return new Something();
    }

    @Provides //unscoped, each INJECT call creates a new instance
    public AnotherThing anotherThing() {
        return new AnotherThing();
    }
}
  • スコープされた依存関係のサブスコープを可能にします。これにより、「スーパースコープ」コンポーネントから提供されたインスタンスを使用する「サブスコープ」コンポーネントを作成できます。

これは、@Subcomponentアノテーションまたはコンポーネントの依存関係を使用して行うことができます。私は個人的に依存関係を好みます。

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);

    ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method
}

@Subcomponent(modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

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

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));

または、コンポーネントの依存関係を次のように使用できます

@Component(modules={ApplicationModule.class})
@ApplicationScope
public class ApplicationComponent {
    Something something(); 
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent extends ApplicationComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

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

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();

知っておくべき重要なこと:

  • スコーププロバイダーは、コンポーネントごとに、指定されたスコープのインスタンスを1つ作成します。コンポーネントが独自のインスタンスを追跡することを意味しますが、他のコンポーネントには共有スコーププールや魔法はありません。特定のスコープにインスタンスを1つ作成するには、コンポーネントのインスタンスが1つ必要です。これがApplicationComponent、独自のスコープ依存関係にアクセスするためにを提供する必要がある理由です。

  • コンポーネントは1つのスコープコンポーネントのみをサブスコープできます。複数のスコープコンポーネントの依存関係は許可されていません。


コンポーネントは1つのスコープコンポーネントのみをサブスコープできます。複数のスコープコンポーネントの依存関係は許可されていません(それらがすべて異なるスコープである場合でも、それはバグだと思います)。それが何を意味するのか本当に理解していない
デイモンユアン

しかし、ライフサイクルについてはどうでしょう。アクティビティが破棄された場合、ActivityComponentはガベージコレクターの候補になりますか?
Severの

別の場所に保管しない場合は、はい
EpicPandaForce

1
したがって、Activityを介してライブでコンポーネントと注入されたオブジェクトが必要な場合は、Activity内にコンポーネントを構築します。Fragmentだけで生き残りたい場合は、フラグメント内にコンポーネントを作成する必要がありますよね?コンポーネントインスタンスを保持する場所はスコープになりますか?
トラキア

特定のアクティビティで生き残りたい場合はどうすればよいですか?
トラキア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.