140

私は、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 アノテーションはルート グラフとカスタム グラフの両方に使用されるため、実際のスコープが何であるかを把握するのは困難です。

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

私の理解

ダガー 1.x

依存関係はどちら@Singletonかでした。これは、ルート グラフとサブグラフの依存関係にも同様に当てはまり、依存関係がどのグラフにバインドされているかについてあいまいさをもたらしました (「In Dagger are Singletons within the subgraph cached or will they always be recreated when a new activity sub-graphs.構築されていますか?

ダガー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ですが、現在はアプリケーション レベルのシングルトン インスタンスを対象としており、カスタム スコープは、より短いライフサイクルでシングルトンの依存関係に注釈を付けるための推奨される方法です。

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

64,000 ドルの質問*

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

* 実際には 64,000 ドルの質問ではありません。

4

1 に答える 1

73

あなたの質問について

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 つだけサブスコープできます。複数のスコープ コンポーネントの依存関係は許可されていません。

于 2015-08-26T13:21:04.817 に答える